﻿#if UNITY_EDITOR
#if UNITY_2017_3_OR_NEWER
	#define AVPRO_MOVIECAPTURE_OFFLINE_AUDIOCAPTURE
#endif
#if UNITY_5_4_OR_NEWER || (UNITY_5 && !UNITY_5_0)
	#define AVPRO_MOVIECAPTURE_WINDOWTITLE_51
	#define AVPRO_MOVIECAPTURE_GRAPHICSDEVICETYPE_51
#endif
#if UNITY_5_4_OR_NEWER || (UNITY_5 && !UNITY_5_0 && !UNITY_5_1 && !UNITY_5_2)
	#define AVPRO_MOVIECAPTURE_SCENEMANAGER_53
#endif
#if UNITY_5_4_OR_NEWER || UNITY_5
	#define AVPRO_MOVIECAPTURE_DEFERREDSHADING
#endif
#if UNITY_2017_1_OR_NEWER
	#define AVPRO_MOVIECAPTURE_PLAYABLES_SUPPORT
#endif
#if UNITY_2018_1_OR_NEWER
	// Unity 2018.1 introduces stereo cubemap render methods
	#define AVPRO_MOVIECAPTURE_UNITY_STEREOCUBEMAP_RENDER
#endif
#if !UNITY_2018_3_OR_NEWER
	#define SUPPORT_SCENE_VIEW_GIZMOS_CAPTURE
#endif

using UnityEngine;
using UnityEditor;

//-----------------------------------------------------------------------------
// Copyright 2012-2021 RenderHeads Ltd.  All rights reserved.
//-----------------------------------------------------------------------------

namespace RenderHeads.Media.AVProMovieCapture.Editor
{
	/// <summary>
	/// Creates a dockable window in Unity that can be used for handy in-editor capturing
	/// </summary>
	public class CaptureEditorWindow : EditorWindow
	{
		private const string TempGameObjectName = "Temp97435_MovieCapture";
		private const string SettingsPrefix = "AVProMovieCapture.EditorWindow.";
		private const string SelectorPrefix = SettingsPrefix + "CameraSelector.";

		private GameObject _gameObject;
		private CaptureBase _capture;
		private CaptureFromScreen _captureScreen;
		private CaptureFromCamera _captureCamera;
		private CaptureFromCamera360 _captureCamera360;
		private CaptureFromCamera360ODS _captureCamera360ODS;
		private CameraSelector _cameraSelector;
#if AVPRO_MOVIECAPTURE_PLAYABLES_SUPPORT
		private TimelineController _timelineController;
#endif

		private static bool _isTrialVersion = false;
		private static bool _isCreated = false;
		private static bool _isInit = false;
		private static bool _isFailedInit = false;
		private static bool _showAlpha = false;
		private static string[] _fileExtensions = new string[0];
		private static string[] _audioDeviceNames = new string[0];

		private readonly string[] _downScales = { "Original", "Half", "Quarter", "Eighth", "Sixteenth", "Custom" };
		private readonly string[] _captureModes = { "Realtime Capture", "Offline Render" };
		private readonly string[] _outputFolders = { "Project Folder", "Persistent Data Folder", "Absolute Folder", "Desktop" };
		private readonly string[] _sourceNames = { "Screen", "Camera", "Camera 360 (Mono+Stereo)", "Camera 360 (experimental ODS Stereo)" };
		private readonly string[] _tabNames = { "Capture", "Visual", "Audio", "Encoding" };

		private readonly static GUIContent _guiCameraSelectorTag = new GUIContent("Tag");
		private readonly static GUIContent _guiCameraSelectorName = new GUIContent("Name");
		private readonly static GUIContent _guiContributingCameras = new GUIContent("Contributing Cameras");
		private readonly static GUIContent _guiCaptureWorldSpaceUI= new GUIContent("Capture Worldspace UI");
		private readonly static GUIContent _guiCameraRotation = new GUIContent("Camera Rotation");
		private readonly static GUIContent _guiInterpupillaryDistance = new GUIContent("Interpupillary distance");
		private readonly static GUIContent _guiStartDelay = new GUIContent("Start Delay");
		private readonly static GUIContent _guiSeconds = new GUIContent("Seconds");
		private readonly static GUIContent _guiStartFrame = new GUIContent("Start Frame");
		private readonly static GUIContent _guiZeroDigits = new GUIContent("Zero Digits");

		private enum SourceType
		{
			Screen,
			Camera,
			Camera360,
			Camera360ODS,
		}

		private enum ConfigTabs
		{
			Capture = 0,
			Visual = 1,
			Audio = 2,
			Encoding = 3,
		}

		[SerializeField] SourceType _sourceType = SourceType.Screen;

		private Camera _cameraNode;
		private string _cameraName;
		private int _captureModeIndex;
		private int _outputFolderIndex;

		[SerializeField] OutputTarget _outputTarget = OutputTarget.VideoFile;
		[SerializeField] ImageSequenceFormat _imageSequenceFormat = ImageSequenceFormat.PNG;
		private bool _filenamePrefixFromSceneName = true;
		private string _filenamePrefix = "capture";
		private string _filenameExtension = "mp4";
		private int _fileContainerIndex = 0;

		[SerializeField] int _imageSequenceStartFrame = 0;
		[SerializeField, Range(2, 12)] int _imageSequenceZeroDigits = 6;
		private string _outputFolderRelative = "Captures";
		private string _outputFolderAbsolute = string.Empty;
		private bool _appendTimestamp = true;

		[SerializeField] string _namedPipePath = @"\\.\pipe\pipename";

		private int _downScaleIndex;
		private int _downscaleX;
		private int _downscaleY;

		private bool _captureMouseCursor = false;
		private Texture2D _mouseCursorTexture = null;

		[SerializeField] CaptureBase.Resolution _renderResolution = CaptureBase.Resolution.Original;
		private Vector2 _renderSize;
		[SerializeField] int _renderAntiAliasing;

		[SerializeField] bool _useContributingCameras = true;
		[SerializeField] float _frameRate = 30f;
		[SerializeField] int _timelapseScale = 1;

		private AudioCaptureSource _audioCaptureSource = AudioCaptureSource.None;
		[SerializeField, Range(8000, 96000)] int _manualAudioSampleRate = 48000;
		[SerializeField, Range(1, 8)] int _manualAudioChannelCount = 2;
		private Vector2 _scroll = Vector2.zero;
		private bool _queueStart;
		private Codec _queueConfigureVideoCodec = null;
		private Codec _queueConfigureAudioCodec = null;

		private bool _useMotionBlur = false;
		private int _motionBlurSampleCount = 16;

		private int _cubemapResolution = 2048;
		private int _cubemapDepth = 24;
		[SerializeField] bool _render180Degrees = false;
		[SerializeField] bool _captureWorldSpaceGUI = false;
		[SerializeField] bool _supportCameraRotation = false;
		[SerializeField] bool _onlyLeftRightRotation = false;
		private int _cubemapStereoPacking = 0;
		private float _cubemapStereoIPD = 0.064f;

		[SerializeField] StartDelayMode _startDelay = StartDelayMode.None;
		[SerializeField] float _startDelaySeconds = 0f;

		[SerializeField] StopMode _stopMode = StopMode.None;
		private int _stopFrames = 300;
		private float _stopSeconds = 10f;

		[SerializeField] CameraSelector.SelectByMode _selectBy = CameraSelector.SelectByMode.HighestDepthCamera;
		[SerializeField] CameraSelector.ScanFrequencyMode _scanFrequency = CameraSelector.ScanFrequencyMode.SceneLoad;
		[SerializeField] bool _scanHiddenCameras = false;
		[SerializeField] string _selectCameraTag = "MainCamera";
		[SerializeField] string _selectCameraName = "Main Camera";

		private SerializedProperty _propCameraSelectorSelectBy;
		private SerializedProperty _propCameraSelectorScanFrequency;
		private SerializedProperty _propCameraSelectorScanHiddenCameras;
		private SerializedProperty _propCameraSelectorTag;
		private SerializedProperty _propCameraSelectorName;

		private SerializedProperty _propSourceType;
		private SerializedProperty _propOutputTarget;
		private SerializedProperty _propImageSequenceFormat;
		private SerializedProperty _propImageSequenceStartFrame;
		private SerializedProperty _propImageSequenceZeroDigits;
		private SerializedProperty _propNamedPipePath;
		private SerializedProperty _propFrameRate;
		[Tooltip("Timelapse scale makes the frame capture run at a fraction of the target frame rate.  Default value is 1")]
		private SerializedProperty _propTimelapseScale;
		private SerializedProperty _propStartDelay;
		private SerializedProperty _propStartDelaySeconds;
		private SerializedProperty _propStopMode;
		private SerializedProperty _propRenderResolution;
		private SerializedProperty _propUseContributingCameras;
		private SerializedProperty _propRender180Degrees;
		private SerializedProperty _propCaptureWorldSpaceGUI;
		private SerializedProperty _propSupportCameraRotation;
		private SerializedProperty _propOnlyLeftRightRotation;
		private SerializedProperty _propManualAudioSampleRate;
		private SerializedProperty _propManualAudioChannelCount;

		private SerializedProperty _propRenderAntiAliasing;
		private SerializedProperty _propOdsRender180Degrees;
		private SerializedProperty _propOdsCamera;
		private SerializedProperty _propOdsIPD;
		private SerializedProperty _propOdsPixelSliceSize;
		private SerializedProperty _propOdsPaddingSize;
		private SerializedProperty _propOdsCameraClearMode;
		private SerializedProperty _propOdsCameraClearColor;

		[SerializeField] CaptureFromCamera360ODS.Settings _odsSettings = new CaptureFromCamera360ODS.Settings();

		private SerializedProperty _propVideoHintsAverageBitrate;
#if UNITY_EDITOR_WIN
		private SerializedProperty _propVideoHintsMaximumBitrate;
#endif
		private SerializedProperty _propVideoHintsQuality;
		private SerializedProperty _propVideoHintsKeyframeInterval;
		private SerializedProperty _propVideoHintsAllowFastStart;
		private SerializedProperty _propVideoHintsSupportTransparency;
#if UNITY_EDITOR_WIN
		private SerializedProperty _propVideoHintsUseHardwareEncoding;
#endif
#if UNITY_EDITOR_OSX
		private SerializedProperty _propImageHintsQuality;
#endif
		private SerializedProperty _propImageHintsSupportTransparency;

		[SerializeField] EncoderHints _encoderHints = new EncoderHints();

		// TODO: we should actually be saving these parameters per-scene...

		private Codec _videoCodec = null;
		private Codec _audioCodec = null;
		private Device _audioInputDevice = null;

		private long _lastFileSize;
		private uint _lastEncodedMinutes;
		private uint _lastEncodedSeconds;
		private uint _lastEncodedFrame;
		private int _selectedTool;
		private int _selectedConfigTab;
		private bool _expandSectionTrial = true;

		private static Texture2D _icon;
		private string _pluginVersionWarningText = string.Empty;

		private SerializedObject _so;

		private const string LinkPluginWebsite = "http://renderheads.com/products/avpro-movie-capture/";
		private const string LinkForumPage = "http://forum.unity3d.com/threads/released-avpro-movie-capture.120717/";
		private const string LinkAssetStorePage = "https://assetstore.unity.com/packages/tools/video/avpro-movie-capture-151061?aid=1101lcNgx";
		private const string LinkSupport = "https://github.com/RenderHeads/UnityPlugin-AVProMovieCapture/issues";
		private const string LinkUserManual = "http://downloads.renderheads.com/docs/UnityAVProMovieCapture.pdf";

		private const string SupportMessage = "If you are reporting a bug, please include any relevant files and details so that we may remedy the problem as fast as possible.\n\n" +
			"Essential details:\n" +
			"+ Error message\n" +
			"      + The exact error message\n" +
			"      + The console/output log if possible\n" +
			"+ Development environment\n" +
			"      + Unity version\n" +
			"      + Development OS version\n" +
			"      + AVPro Movie Capture plugin version\n";

		[MenuItem("Window/Open AVPro Movie Capture..")]
		public static void Init()
		{
			if (_isInit || _isCreated)
			{
				CaptureEditorWindow window = (CaptureEditorWindow)EditorWindow.GetWindow(typeof(CaptureEditorWindow));
				window.Close();
				return;
			}

			_isCreated = true;

			// Get existing open window or if none, make a new one:
			CaptureEditorWindow window2 = (CaptureEditorWindow)EditorWindow.GetWindow(typeof(CaptureEditorWindow));
			if (window2 != null)
			{
				window2.SetupWindow();
			}
		}

		public void SetupWindow()
		{
			_isCreated = true;
			if ((Application.platform == RuntimePlatform.WindowsEditor)
			||  (Application.platform == RuntimePlatform.OSXEditor))
			{
				this.minSize = new Vector2(200f, 48f);
				this.maxSize = new Vector2(340f, 620f);
#if AVPRO_MOVIECAPTURE_WINDOWTITLE_51
				if (_icon != null)
				{
					this.titleContent = new GUIContent("Movie Capture", _icon, "AVPro Movie Capture");
				}
				else
				{
					this.titleContent = new GUIContent("Movie Capture", "AVPro Movie Capture");
				}
#else
				this.title = "Movie Capture";
#endif
				this.CreateGUI();
				this.LoadSettings();

				_so = new SerializedObject(this);
				if (_so == null)
				{
					Debug.LogError("[AVProMovieCapture] SO is null");
				}

				_propSourceType = _so.FindProperty("_sourceType");
				_propOutputTarget = _so.FindProperty("_outputTarget");
				_propImageSequenceFormat = _so.FindProperty("_imageSequenceFormat");
				_propImageSequenceStartFrame = _so.FindProperty("_imageSequenceStartFrame");
				_propImageSequenceZeroDigits = _so.FindProperty("_imageSequenceZeroDigits");
				_propNamedPipePath = _so.FindProperty("_namedPipePath");
				_propRenderResolution = _so.FindProperty("_renderResolution");
				_propUseContributingCameras = _so.FindProperty("_useContributingCameras");
				_propRender180Degrees = _so.FindProperty("_render180Degrees");
				_propCaptureWorldSpaceGUI = _so.FindProperty("_captureWorldSpaceGUI");
				_propSupportCameraRotation = _so.FindProperty("_supportCameraRotation");
				_propOnlyLeftRightRotation = _so.FindProperty("_onlyLeftRightRotation");

				// Audio
				_propManualAudioSampleRate = _so.FindProperty("_manualAudioSampleRate");
				_propManualAudioChannelCount = _so.FindProperty("_manualAudioChannelCount");

				// Time
				_propFrameRate = _so.FindProperty("_frameRate");
				_propTimelapseScale = _so.FindProperty("_timelapseScale");

				// Start/Stop
				_propStopMode = _so.FindProperty("_stopMode");
				_propStartDelay = _so.FindProperty("_startDelay");
				_propStartDelaySeconds = _so.FindProperty("_startDelaySeconds");

				// Camera Selector
				_propCameraSelectorSelectBy = _so.FindProperty("_selectBy");
				_propCameraSelectorScanFrequency = _so.FindProperty("_scanFrequency");
				_propCameraSelectorScanHiddenCameras = _so.FindProperty("_scanHiddenCameras");
				_propCameraSelectorTag = _so.FindProperty("_selectCameraTag");
				_propCameraSelectorName = _so.FindProperty("_selectCameraName");

				_propRenderAntiAliasing = _so.FindProperty("_renderAntiAliasing");
				_propOdsIPD = _so.FindProperty("_odsSettings.ipd");
				_propOdsRender180Degrees = _so.FindProperty("_odsSettings.render180Degrees");
				_propOdsPixelSliceSize = _so.FindProperty("_odsSettings.pixelSliceSize");
				_propOdsPaddingSize = _so.FindProperty("_odsSettings.paddingSize");
				_propOdsCameraClearMode = _so.FindProperty("_odsSettings.cameraClearMode");
				_propOdsCameraClearColor = _so.FindProperty("_odsSettings.cameraClearColor");

				_propVideoHintsAverageBitrate = _so.FindProperty("_encoderHints.videoHints.averageBitrate");
#if UNITY_EDITOR_WIN
				_propVideoHintsMaximumBitrate = _so.FindProperty("_encoderHints.videoHints.maximumBitrate");
#endif
				_propVideoHintsQuality = _so.FindProperty("_encoderHints.videoHints.quality");
				_propVideoHintsKeyframeInterval = _so.FindProperty("_encoderHints.videoHints.keyframeInterval");
				_propVideoHintsAllowFastStart = _so.FindProperty("_encoderHints.videoHints.allowFastStartStreamingPostProcess");
				_propVideoHintsSupportTransparency = _so.FindProperty("_encoderHints.videoHints.supportTransparency");
#if UNITY_EDITOR_WIN
				_propVideoHintsUseHardwareEncoding = _so.FindProperty("_encoderHints.videoHints.useHardwareEncoding");
#endif
#if UNITY_EDITOR_OSX
				_propImageHintsQuality = _so.FindProperty("_encoderHints.imageHints.quality");
#endif
				_propImageHintsSupportTransparency = _so.FindProperty("_encoderHints.imageHints.supportTransparency");

				this.Repaint();
			}
		}

		private void LoadSettings()
		{
			_sourceType = (SourceType)EditorPrefs.GetInt(SettingsPrefix + "SourceType", (int)_sourceType);

			_cameraName = EditorPrefs.GetString(SettingsPrefix + "CameraName", string.Empty);
			_captureModeIndex = EditorPrefs.GetInt(SettingsPrefix + "CaptureModeIndex", 0);

			_captureMouseCursor = EditorPrefs.GetBool(SettingsPrefix + "CaptureMouseCursor", false);
			string mouseCursorGuid = EditorPrefs.GetString(SettingsPrefix + "CaptureMouseTexture", string.Empty);
			if (!string.IsNullOrEmpty(mouseCursorGuid))
			{
				string mouseCursorPath = AssetDatabase.GUIDToAssetPath(mouseCursorGuid);
				if (!string.IsNullOrEmpty(mouseCursorPath))
				{
					_mouseCursorTexture = (Texture2D)AssetDatabase.LoadAssetAtPath(mouseCursorPath, typeof(Texture2D));
				}
			}

			_outputTarget = (OutputTarget)EditorPrefs.GetInt(SettingsPrefix + "OutputTarget", (int)_outputTarget);
			_imageSequenceFormat = (ImageSequenceFormat)EditorPrefs.GetInt(SettingsPrefix + "ImageSequenceFormat", (int)_imageSequenceFormat);
			_namedPipePath = EditorPrefs.GetString(SettingsPrefix + "NamedPipePath", _namedPipePath);
			_filenamePrefixFromSceneName = EditorPrefs.GetBool(SettingsPrefix + "FilenamePrefixFromScenename", _filenamePrefixFromSceneName);
			_filenamePrefix = EditorPrefs.GetString(SettingsPrefix + "FilenamePrefix", "capture");
			_filenameExtension = EditorPrefs.GetString(SettingsPrefix + "FilenameExtension", _filenameExtension);
			_fileContainerIndex = EditorPrefs.GetInt(SettingsPrefix + "FileContainerIndex", _fileContainerIndex);
			_appendTimestamp = EditorPrefs.GetBool(SettingsPrefix + "AppendTimestamp", true);
			_imageSequenceStartFrame = EditorPrefs.GetInt(SettingsPrefix + "ImageSequenceStartFrame", 0);
			_imageSequenceZeroDigits = EditorPrefs.GetInt(SettingsPrefix + "ImageSequenceZeroDigits", 6);

			_outputFolderIndex = EditorPrefs.GetInt(SettingsPrefix + "OutputFolderIndex", (int)CaptureBase.OutputPath.RelativeToProject);
			_outputFolderRelative = EditorPrefs.GetString(SettingsPrefix + "OutputFolderRelative", "Captures");
			_outputFolderAbsolute = EditorPrefs.GetString(SettingsPrefix + "OutputFolderAbsolute", string.Empty);

			_downScaleIndex = EditorPrefs.GetInt(SettingsPrefix + "DownScaleIndex", 0);
			_downscaleX = EditorPrefs.GetInt(SettingsPrefix + "DownScaleX", 1);
			_downscaleY = EditorPrefs.GetInt(SettingsPrefix + "DownScaleY", 1);
			_frameRate = EditorPrefs.GetFloat(SettingsPrefix + "FrameRate", _frameRate);
			_timelapseScale = EditorPrefs.GetInt(SettingsPrefix + "TimelapseScale", 1);

			_renderResolution = (CaptureBase.Resolution)EditorPrefs.GetInt(SettingsPrefix + "RenderResolution", (int)_renderResolution);
			_renderSize.x = EditorPrefs.GetInt(SettingsPrefix + "RenderWidth", 0);
			_renderSize.y = EditorPrefs.GetInt(SettingsPrefix + "RenderHeight", 0);
			_renderAntiAliasing = EditorPrefs.GetInt(SettingsPrefix + "RenderAntiAliasing", 0);
			_useContributingCameras = EditorPrefs.GetBool(SettingsPrefix + "UseContributingCameras", true);

			_audioCaptureSource = (AudioCaptureSource)EditorPrefs.GetInt(SettingsPrefix + "AudioCaptureSource", (int)_audioCaptureSource);
			_audioInputDevice = DeviceManager.AudioInputDevices.FindDevice(EditorPrefs.GetString(SettingsPrefix + "AudioInputDeviceName", ""));
			_manualAudioChannelCount = Mathf.Clamp(EditorPrefs.GetInt(SettingsPrefix + "ManualAudioChannelCount", (int)_manualAudioChannelCount), 1, 8);
			_manualAudioSampleRate = Mathf.Clamp(EditorPrefs.GetInt(SettingsPrefix + "ManualAudioSampleRate", (int)_manualAudioSampleRate), 8000, 96000);

			_useMotionBlur = EditorPrefs.GetBool(SettingsPrefix + "UseMotionBlur", false);
			_motionBlurSampleCount = EditorPrefs.GetInt(SettingsPrefix + "MotionBlurSampleCount", 16);

			_render180Degrees = EditorPrefs.GetBool(SettingsPrefix + "Render180Degrees", false);
			_captureWorldSpaceGUI = EditorPrefs.GetBool(SettingsPrefix + "CaptureWorldSpaceGUI", false);
			_supportCameraRotation = EditorPrefs.GetBool(SettingsPrefix + "SupportCameraRotation", false);
			_onlyLeftRightRotation = EditorPrefs.GetBool(SettingsPrefix + "OnlyLeftRightRotation", false);
			_cubemapResolution = EditorPrefs.GetInt(SettingsPrefix + "CubemapResolution", 2048);
			_cubemapDepth = EditorPrefs.GetInt(SettingsPrefix + "CubemapDepth", 24);
			_cubemapStereoPacking = EditorPrefs.GetInt(SettingsPrefix + "CubemapStereoPacking", 0);
			_cubemapStereoIPD = EditorPrefs.GetFloat(SettingsPrefix + "CubemapStereoIPD", 0.064f);

			_startDelay = (StartDelayMode)EditorPrefs.GetInt(SettingsPrefix + "StartDelay", (int)_startDelay);
			_startDelaySeconds = EditorPrefs.GetFloat(SettingsPrefix + "StartDelaySeconds", _startDelaySeconds);

			_stopMode = (StopMode)EditorPrefs.GetInt(SettingsPrefix + "StopMode", (int)_stopMode);
			_stopFrames = EditorPrefs.GetInt(SettingsPrefix + "StopFrames", _stopFrames);
			_stopSeconds = EditorPrefs.GetFloat(SettingsPrefix + "StopSeconds", _stopSeconds);

			_encoderHints.videoHints.averageBitrate = (uint)EditorPrefs.GetInt(SettingsPrefix + "EncoderHints.VideoHints.AverageBitrate", (int)_encoderHints.videoHints.averageBitrate);
			_encoderHints.videoHints.maximumBitrate = (uint)EditorPrefs.GetInt(SettingsPrefix + "EncoderHints.VideoHints.MaximumBitrate", (int)_encoderHints.videoHints.maximumBitrate);
			_encoderHints.videoHints.quality = EditorPrefs.GetFloat(SettingsPrefix + "EncoderHints.VideoHints.Quality", _encoderHints.videoHints.quality);
			_encoderHints.videoHints.keyframeInterval = (uint)EditorPrefs.GetInt(SettingsPrefix + "EncoderHints.VideoHints.KeyframeInterval", (int)_encoderHints.videoHints.keyframeInterval);
			_encoderHints.videoHints.allowFastStartStreamingPostProcess = EditorPrefs.GetBool(SettingsPrefix + "EncoderHints.VideoHints.AllowFastStart", _encoderHints.videoHints.allowFastStartStreamingPostProcess);
			_encoderHints.videoHints.supportTransparency = EditorPrefs.GetBool(SettingsPrefix + "EncoderHints.VideoHints.SupportTransparency", _encoderHints.videoHints.supportTransparency);
			_encoderHints.videoHints.useHardwareEncoding = EditorPrefs.GetBool(SettingsPrefix + "EncoderHints.VideoHints.UseHardwareEncoding", _encoderHints.videoHints.useHardwareEncoding);
			_encoderHints.imageHints.quality = EditorPrefs.GetFloat(SettingsPrefix + "EncoderHints.ImageHints.Quality", _encoderHints.imageHints.quality);
			_encoderHints.imageHints.supportTransparency = EditorPrefs.GetBool(SettingsPrefix + "EncoderHints.ImageHints.SupportTransparency", _encoderHints.imageHints.supportTransparency);

			if (!string.IsNullOrEmpty(_cameraName))
			{
				Camera[] cameras = (Camera[])GameObject.FindObjectsOfType(typeof(Camera));
				foreach (Camera cam in cameras)
				{
					if (cam.name == _cameraName)
					{
						_cameraNode = cam;
						break;
					}
				}
			}

			_showAlpha = EditorPrefs.GetBool(SettingsPrefix + "ShowAlphaChannel", false);

			// Codecs
			_videoCodec = CodecManager.VideoCodecs.FindCodec(EditorPrefs.GetString(SettingsPrefix + "VideoCodecName", ""));
			_audioCodec = CodecManager.AudioCodecs.FindCodec(EditorPrefs.GetString(SettingsPrefix + "AudioCodecName", ""));
			UpdateSelectedCodec();

			// Camera selector
			_selectBy = (CameraSelector.SelectByMode)EditorPrefs.GetInt(SelectorPrefix + "SelectBy", (int)_selectBy);
			_scanFrequency = (CameraSelector.ScanFrequencyMode)EditorPrefs.GetInt(SelectorPrefix + "ScanFrequency", (int)_scanFrequency);
			_scanHiddenCameras = EditorPrefs.GetBool(SelectorPrefix + "ScanHiddenCameras", _scanHiddenCameras);
			_selectCameraTag = EditorPrefs.GetString(SelectorPrefix + "Tag", _selectCameraTag);
			_selectCameraName = EditorPrefs.GetString(SelectorPrefix + "Name", _selectCameraName);
		}

		private void SaveSettings()
		{
			EditorPrefs.SetInt(SettingsPrefix + "SourceType", (int)_sourceType);
			EditorPrefs.SetString(SettingsPrefix + "CameraName", _cameraName);
			EditorPrefs.SetInt(SettingsPrefix + "CaptureModeIndex", _captureModeIndex);
			EditorPrefs.SetBool(SettingsPrefix + "CaptureMouseCursor", _captureMouseCursor);
			string mouseCursorGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(_mouseCursorTexture));
			EditorPrefs.SetString(SettingsPrefix + "CaptureMouseTexture", mouseCursorGuid);

			EditorPrefs.SetInt(SettingsPrefix + "OutputTarget", (int)_outputTarget);
			EditorPrefs.SetInt(SettingsPrefix + "ImageSequenceFormat", (int)_imageSequenceFormat);
			EditorPrefs.SetString(SettingsPrefix + "NamedPipePath", _namedPipePath);
			EditorPrefs.SetBool(SettingsPrefix + "FilenamePrefixFromScenename", _filenamePrefixFromSceneName);
			EditorPrefs.SetString(SettingsPrefix + "FilenamePrefix", _filenamePrefix);
			EditorPrefs.SetString(SettingsPrefix + "FilenameExtension", _filenameExtension);
			EditorPrefs.SetInt(SettingsPrefix + "FileContainerIndex", _fileContainerIndex);
			EditorPrefs.SetBool(SettingsPrefix + "AppendTimestamp", _appendTimestamp);
			EditorPrefs.SetInt(SettingsPrefix + "ImageSequenceStartFrame", _imageSequenceStartFrame);
			EditorPrefs.SetInt(SettingsPrefix + "ImageSequenceZeroDigits", _imageSequenceZeroDigits);

			EditorPrefs.SetInt(SettingsPrefix + "OutputFolderIndex", _outputFolderIndex);
			EditorPrefs.SetString(SettingsPrefix + "OutputFolderRelative", _outputFolderRelative);
			EditorPrefs.SetString(SettingsPrefix + "OutputFolderAbsolute", _outputFolderAbsolute);

			EditorPrefs.SetInt(SettingsPrefix + "DownScaleIndex", _downScaleIndex);
			EditorPrefs.SetInt(SettingsPrefix + "DownScaleX", _downscaleX);
			EditorPrefs.SetInt(SettingsPrefix + "DownScaleY", _downscaleY);
			EditorPrefs.SetFloat(SettingsPrefix + "FrameRate", _frameRate);
			EditorPrefs.SetInt(SettingsPrefix + "TimelapseScale", _timelapseScale);

			EditorPrefs.SetInt(SettingsPrefix + "RenderResolution", (int)_renderResolution);
			EditorPrefs.SetInt(SettingsPrefix + "RenderWidth", (int)_renderSize.x);
			EditorPrefs.SetInt(SettingsPrefix + "RenderHeight", (int)_renderSize.y);
			EditorPrefs.SetInt(SettingsPrefix + "RenderAntiAliasing", _renderAntiAliasing);
			EditorPrefs.SetBool(SettingsPrefix + "UseContributingCameras", _useContributingCameras);

			EditorPrefs.SetString(SettingsPrefix + "VideoCodecName", (_videoCodec != null)?_videoCodec.Name:string.Empty);
			EditorPrefs.SetString(SettingsPrefix + "AudioCodecName", (_audioCodec != null)?_audioCodec.Name:string.Empty);

			EditorPrefs.SetInt(SettingsPrefix + "AudioCaptureSource", (int)_audioCaptureSource);
			EditorPrefs.SetString(SettingsPrefix + "AudioInputDeviceName", (_audioInputDevice != null)?_audioInputDevice.Name:string.Empty);
			EditorPrefs.SetInt(SettingsPrefix + "ManualAudioChannelCount", _manualAudioChannelCount);
			EditorPrefs.SetInt(SettingsPrefix + "ManualAudioSampleRate", _manualAudioSampleRate);

			EditorPrefs.SetBool(SettingsPrefix + "UseMotionBlur", _useMotionBlur);
			EditorPrefs.SetInt(SettingsPrefix + "MotionBlurSampleCount", _motionBlurSampleCount);

			EditorPrefs.SetBool(SettingsPrefix + "Render180Degrees", _render180Degrees);
			EditorPrefs.SetBool(SettingsPrefix + "CaptureWorldSpaceGUI", _captureWorldSpaceGUI);
			EditorPrefs.SetBool(SettingsPrefix + "SupportCameraRotation", _supportCameraRotation);
			EditorPrefs.SetBool(SettingsPrefix + "OnlyLeftRightRotation", _onlyLeftRightRotation);
			EditorPrefs.SetInt(SettingsPrefix + "CubemapResolution", _cubemapResolution);
			EditorPrefs.SetInt(SettingsPrefix + "CubemapDepth", _cubemapDepth);
			EditorPrefs.SetInt(SettingsPrefix + "CubemapStereoPacking", _cubemapStereoPacking);
			EditorPrefs.SetFloat(SettingsPrefix + "CubemapStereoIPD", _cubemapStereoIPD);

			EditorPrefs.SetInt(SettingsPrefix + "StartDelay", (int)_startDelay);
			EditorPrefs.SetFloat(SettingsPrefix + "StartDelaySeconds", _startDelaySeconds);

			EditorPrefs.SetInt(SettingsPrefix + "StopMode", (int)_stopMode);
			EditorPrefs.SetInt(SettingsPrefix + "StopFrames", _stopFrames);
			EditorPrefs.SetFloat(SettingsPrefix + "StopSeconds", _stopSeconds);

			EditorPrefs.SetInt(SettingsPrefix + "EncoderHints.VideoHints.AverageBitrate", (int)_encoderHints.videoHints.averageBitrate);
			EditorPrefs.SetInt(SettingsPrefix + "EncoderHints.VideoHints.MaximumBitrate", (int)_encoderHints.videoHints.maximumBitrate);
			EditorPrefs.SetFloat(SettingsPrefix + "EncoderHints.VideoHints.Quality", _encoderHints.videoHints.quality);
			EditorPrefs.SetInt(SettingsPrefix + "EncoderHints.VideoHints.KeyframeInterval", (int)_encoderHints.videoHints.keyframeInterval);
			EditorPrefs.SetBool(SettingsPrefix + "EncoderHints.VideoHints.AllowFastStart", _encoderHints.videoHints.allowFastStartStreamingPostProcess);
			EditorPrefs.SetBool(SettingsPrefix + "EncoderHints.VideoHints.SupportTransparency", _encoderHints.videoHints.supportTransparency);
			EditorPrefs.SetBool(SettingsPrefix + "EncoderHints.VideoHints.UseHardwareEncoding", _encoderHints.videoHints.useHardwareEncoding);
			EditorPrefs.SetFloat(SettingsPrefix + "EncoderHints.ImageHints.Quality", _encoderHints.imageHints.quality);
			EditorPrefs.SetBool(SettingsPrefix + "EncoderHints.ImageHints.SupportTransparency", _encoderHints.imageHints.supportTransparency);

			EditorPrefs.SetBool(SettingsPrefix + "ShowAlphaChannel", _showAlpha);

			// Camera selector
			EditorPrefs.SetInt(SelectorPrefix + "SelectBy", (int)_selectBy);
			EditorPrefs.SetInt(SelectorPrefix + "ScanFrequency", (int)_scanFrequency);
			EditorPrefs.SetBool(SelectorPrefix + "ScanHiddenCameras", _scanHiddenCameras);
			EditorPrefs.SetString(SelectorPrefix + "Tag", _selectCameraTag);
			EditorPrefs.SetString(SelectorPrefix + "Name", _selectCameraName);
		}

		private void ResetSettings()
		{
			_sourceType = SourceType.Screen;
			_cameraNode = null;
			_cameraName = string.Empty;
			_captureModeIndex = 0;
			_captureMouseCursor = false;
			_mouseCursorTexture = null;
			_outputTarget = OutputTarget.VideoFile;
			_imageSequenceFormat = ImageSequenceFormat.PNG;
			_namedPipePath = @"\\.\pipe\test_pipe";
			_filenamePrefixFromSceneName = true;
			_filenamePrefix = "capture";
			_filenameExtension = "mp4";
			_imageSequenceStartFrame = 0;
			_imageSequenceZeroDigits = 6;
			_outputFolderIndex = (int)CaptureBase.OutputPath.RelativeToProject;
			_outputFolderRelative = "Captures";
			_outputFolderAbsolute = string.Empty;
			_appendTimestamp = true;
			_downScaleIndex = 0;
			_downscaleX = 1;
			_downscaleY = 1;
			_frameRate = 30f;
			_timelapseScale = 1;
			_videoCodec = null;
			_audioCodec = null;
			_renderResolution = CaptureBase.Resolution.Original;
			_renderSize = Vector2.one;
			_renderAntiAliasing = 0;
			_useContributingCameras = true;
			_audioCaptureSource = AudioCaptureSource.None;
			_audioInputDevice = null;
			_manualAudioChannelCount = 2;
			_manualAudioSampleRate = 48000;
			_useMotionBlur = false;
			_motionBlurSampleCount = 16;
			_render180Degrees = false;
			_captureWorldSpaceGUI = false;
			_supportCameraRotation = false;
			_onlyLeftRightRotation = false;
			_cubemapResolution = 2048;
			_cubemapDepth = 24;
			_cubemapStereoPacking = 0;
			_startDelay = StartDelayMode.None;
			_startDelaySeconds = 0f;
			_stopMode = StopMode.None;
			_cubemapStereoIPD = 0.064f;
			_stopFrames = 300;
			_stopSeconds = 10f;
			_encoderHints = new EncoderHints();
			_odsSettings = new CaptureFromCamera360ODS.Settings();

			UpdateSelectedCodec();

			// Camera selector
			_selectBy = CameraSelector.SelectByMode.HighestDepthCamera;
			_scanFrequency = CameraSelector.ScanFrequencyMode.SceneLoad;
			_scanHiddenCameras = false;
			_selectCameraTag = "MainCamera";
			_selectCameraName = "Main Camera";
		}

		private void Configure(CaptureBase capture)
		{
			capture.VideoCodecPriorityWindows = new string[0];
			capture.VideoCodecPriorityMacOS = new string[0];
			capture.AudioCodecPriorityWindows = new string[0];
			capture.AudioCodecPriorityMacOS = new string[0];

			capture.FrameRate = _frameRate;
			capture.TimelapseScale = _timelapseScale;
			capture.ResolutionDownScale = GetDownScaleFromIndex(_downScaleIndex);
			if (capture.ResolutionDownScale == CaptureBase.DownScale.Custom)
			{
				capture.ResolutionDownscaleCustom = new Vector2(_downscaleX, _downscaleY);
			}

			capture.StartDelay = _startDelay;
			capture.StartDelaySeconds = _startDelaySeconds;
			capture.StopMode = _stopMode;
			capture.StopAfterFramesElapsed = _stopFrames;
			capture.StopAfterSecondsElapsed = _stopSeconds;
			capture.SetEncoderHints(_encoderHints);

			capture.IsRealTime = IsCaptureRealTime();

			capture.OutputTarget = _outputTarget;
			if (_outputTarget == OutputTarget.VideoFile)
			{
				capture.FilenamePrefix = _filenamePrefix;
				capture.AppendFilenameTimestamp  = _appendTimestamp;
				capture.AllowManualFileExtension = true;
				capture.FilenameExtension = _filenameExtension;
			}
			else if (_outputTarget == OutputTarget.NamedPipe)
			{
				capture.NamedPipePath = _namedPipePath;
			}
			else if (_outputTarget == OutputTarget.ImageSequence)
			{
				capture.NativeImageSequenceFormat = _imageSequenceFormat;
				capture.ImageSequenceStartFrame = _imageSequenceStartFrame;
				capture.ImageSequenceZeroDigits = _imageSequenceZeroDigits;
				capture.FilenamePrefix = _filenamePrefix;
			}

			if (_outputFolderIndex == (int)CaptureBase.OutputPath.RelativeToPeristentData)
			{
				capture.OutputFolder = CaptureBase.OutputPath.RelativeToPeristentData;
				capture.OutputFolderPath = _outputFolderRelative;
			}
			else if (_outputFolderIndex == (int)CaptureBase.OutputPath.Absolute)
			{
				capture.OutputFolder = CaptureBase.OutputPath.Absolute;
				capture.OutputFolderPath = _outputFolderAbsolute;
			}
			else if (_outputFolderIndex == (int)CaptureBase.OutputPath.RelativeToDesktop)
			{
				capture.OutputFolder = CaptureBase.OutputPath.RelativeToDesktop;
				capture.OutputFolderPath = _outputFolderRelative;
			}
			else
			{
				capture.OutputFolder = CaptureBase.OutputPath.RelativeToProject;
				capture.OutputFolderPath = _outputFolderRelative;
			}

			capture.NativeForceVideoCodecIndex = (_videoCodec != null)?_videoCodec.Index:-1;

			capture.AudioCaptureSource = IsAudioCaptured()?_audioCaptureSource:AudioCaptureSource.None;
			if (capture.AudioCaptureSource != AudioCaptureSource.None)
			{
				capture.NativeForceAudioCodecIndex = (_audioCodec != null)?_audioCodec.Index:-1;
				if (capture.AudioCaptureSource == AudioCaptureSource.Microphone)
				{
					capture.ForceAudioInputDeviceIndex = (_audioInputDevice != null)?_audioInputDevice.Index:-1;
				}
				else if (capture.AudioCaptureSource == AudioCaptureSource.Manual)
				{
					capture.ManualAudioChannelCount = _manualAudioChannelCount;
					capture.ManualAudioSampleRate = _manualAudioSampleRate;
				}
			}

			if (_useMotionBlur && !capture.IsRealTime && Camera.main != null)
			{
				capture.UseMotionBlur = _useMotionBlur;
				capture.MotionBlurSamples = _motionBlurSampleCount;
				capture.MotionBlurCameras = new Camera[1];
				capture.MotionBlurCameras[0] = Camera.main;
			}
			else
			{
				capture.UseMotionBlur = false;
			}

			if (_captureScreen != null)
			{
				// Toggle mouse cursor
				if (_captureMouseCursor)
				{
					_captureScreen.CaptureMouseCursor = true;
					if (_captureScreen.MouseCursor == null)
					{
						_captureScreen.MouseCursor = capture.gameObject.AddComponent<MouseCursor>();
					}
					if (_captureScreen.MouseCursor != null)
					{
						_captureScreen.MouseCursor.SetTexture(_mouseCursorTexture);
					}
				}
				else
				{
					_captureScreen.CaptureMouseCursor = false;
					if (_captureScreen.MouseCursor != null)
					{
						_captureScreen.MouseCursor.enabled = false;
					}
				}
			}
		}

		private void CreateComponents()
		{
			// Create hidden gameobject
			if (_gameObject == null)
			{
				_gameObject = GameObject.Find(TempGameObjectName);
				if (_gameObject == null)
				{
					_gameObject = new GameObject(TempGameObjectName);
					_gameObject.hideFlags = HideFlags.HideAndDontSave;
#if UNITY_5 || UNITY_5_4_OR_NEWER
					_gameObject.hideFlags |= HideFlags.DontSaveInBuild|HideFlags.DontSaveInEditor|HideFlags.DontUnloadUnusedAsset;
#endif
					Object.DontDestroyOnLoad(_gameObject);
				}
			}

			// Remove old capture component if different
			if (_captureScreen != null && _sourceType != SourceType.Screen)
			{
				Destroy(_captureScreen);
				_captureScreen = null;
			}
			if (_captureCamera != null && _sourceType != SourceType.Camera)
			{
				Destroy(_captureCamera);
				_captureCamera = null;
			}
			if (_captureCamera360 != null && _sourceType != SourceType.Camera360)
			{
				Destroy(_captureCamera360);
				_captureCamera360 = null;
			}
			if (_captureCamera360ODS != null && _sourceType != SourceType.Camera360ODS)
			{
				Destroy(_captureCamera360ODS);
				_captureCamera360ODS = null;
			}
			if (_cameraSelector != null && _sourceType == SourceType.Screen)
			{
				Destroy(_cameraSelector);
				_cameraSelector = null;
			}

#if AVPRO_MOVIECAPTURE_PLAYABLES_SUPPORT
			// Remove timelineController for realtime captures
			if (IsCaptureRealTime())
			{
				if (_timelineController != null)
				{
					Destroy(_timelineController);
					_timelineController = null;
				}
			}
			// Add timelineController for non-realtime captures
			else
			{
				if (_timelineController == null)
				{
					_timelineController = _gameObject.AddComponent<TimelineController>();
				}
			}
#endif
			switch (_sourceType)
			{
				case SourceType.Screen:
					if (_captureScreen == null)
					{
						_captureScreen = _gameObject.AddComponent<CaptureFromScreen>();
					}
					_capture = _captureScreen;
					break;
				case SourceType.Camera:
					if (_captureCamera == null)
					{
						_captureCamera = _gameObject.AddComponent<CaptureFromCamera>();
					}
					if (_cameraSelector == null)
					{
						_cameraSelector = _gameObject.AddComponent<CameraSelector>();
					}
					SetupCameraSelector();
					_captureCamera.SetCamera(_cameraNode, _useContributingCameras);
					_captureCamera.CameraSelector = _cameraSelector;
					_capture = _captureCamera;
					_capture.CameraRenderResolution = _renderResolution;
					_capture.CameraRenderCustomResolution = _renderSize;
					_capture.CameraRenderAntiAliasing = _renderAntiAliasing;
					break;
				case SourceType.Camera360:
					if (_captureCamera360 == null)
					{
						_captureCamera360 = _gameObject.AddComponent<CaptureFromCamera360>();
					}
					if (_cameraSelector == null)
					{
						_cameraSelector = _gameObject.AddComponent<CameraSelector>();
					}
					SetupCameraSelector();
					_capture = _captureCamera360;
					_capture.CameraRenderResolution = _renderResolution;
					_capture.CameraRenderCustomResolution = _renderSize;
					_capture.CameraRenderAntiAliasing = _renderAntiAliasing;
					_captureCamera360.SetCamera(_cameraNode);
					_captureCamera360.CameraSelector = _cameraSelector;
					_captureCamera360.Render180Degrees = _render180Degrees;
					_captureCamera360.SupportCameraRotation = _supportCameraRotation;
					_captureCamera360.OnlyLeftRightRotation = _onlyLeftRightRotation;
					_captureCamera360.SupportGUI = _captureWorldSpaceGUI;
					_captureCamera360.CubemapFaceResolution = (CaptureBase.CubemapResolution)_cubemapResolution;
					_captureCamera360.CubemapDepthResolution = (CaptureBase.CubemapDepth)_cubemapDepth;
					_captureCamera360.StereoRendering = (StereoPacking)_cubemapStereoPacking;
					_captureCamera360.IPD = _cubemapStereoIPD;
					break;
				case SourceType.Camera360ODS:
					if (_captureCamera360ODS == null)
					{
						_captureCamera360ODS = _gameObject.AddComponent<CaptureFromCamera360ODS>();
					}
					if (_cameraSelector == null)
					{
						_cameraSelector = _gameObject.AddComponent<CameraSelector>();
					}
					SetupCameraSelector();
					_capture = _captureCamera360ODS;
					_capture.CameraRenderResolution = _renderResolution;
					_capture.CameraRenderCustomResolution = _renderSize;
					_capture.CameraRenderAntiAliasing = _renderAntiAliasing;
					_captureCamera360ODS.Setup.camera = _cameraNode;
					_captureCamera360ODS.Setup.cameraSelector = _cameraSelector;
					_captureCamera360ODS.Setup.render180Degrees = _odsSettings.render180Degrees;
					_captureCamera360ODS.Setup.ipd = _odsSettings.ipd;
					_captureCamera360ODS.Setup.pixelSliceSize = _odsSettings.pixelSliceSize;
					_captureCamera360ODS.Setup.paddingSize = _odsSettings.paddingSize;
					_captureCamera360ODS.Setup.cameraClearMode = _odsSettings.cameraClearMode;
					_captureCamera360ODS.Setup.cameraClearColor= _odsSettings.cameraClearColor;
					break;
			}
#if AVPRO_MOVIECAPTURE_PLAYABLES_SUPPORT
			if (_capture != null)
			{
				_capture.TimelineController = _timelineController;
			}
#endif
		}

		private void SetupCameraSelector()
		{
			if (_cameraSelector == null) return;

			_cameraSelector.SelectBy = _selectBy;
			_cameraSelector.ScanFrequency = _scanFrequency;
			_cameraSelector.ScanHiddenCameras = _scanHiddenCameras;
			if (_selectBy == CameraSelector.SelectByMode.Tag)
			{
				_cameraSelector.SelectTag = _selectCameraTag;
			}
			else if (_selectBy == CameraSelector.SelectByMode.Name)
			{
				_cameraSelector.SelectName = _selectCameraName;
			}
			else if (_selectBy == CameraSelector.SelectByMode.Manual)
			{
				_cameraSelector.Camera = _cameraNode;
			}
		}

		private void CreateGUI()
		{
			try
			{
				if (!NativePlugin.Init())
				{
					Debug.LogError("[AVProMovieCapture] Failed to initialise");
					return;
				}
			}
			catch (System.DllNotFoundException e)
			{
				_isFailedInit = true;
				string missingDllMessage = string.Empty;
#if (UNITY_5 || UNITY_5_4_OR_NEWER)
				missingDllMessage = "Unity couldn't find the plugin DLL. Please select the native plugin files in 'Plugins/RenderHeads/AVProMovieCapture/Plugins' folder and select the correct platform in the Inspector.";
#else
				missingDllMessage = "Unity couldn't find the plugin DLL, Unity 4.x requires the 'Plugins' folder to be at the root of your project.  Please move the contents of the 'Plugins' folder (in Plugins/RenderHeads/AVProMovieCapture/Plugins) to the 'Plugins' folder in the root of your project.";
#endif
				Debug.LogError("[AVProMovieCapture] " + missingDllMessage);
#if UNITY_EDITOR
				UnityEditor.EditorUtility.DisplayDialog("Plugin files not found", missingDllMessage, "Ok");
#endif
				throw e;
			}

			// Audio device enumeration
			{
				int numAudioDevices = Mathf.Max(0, NativePlugin.GetAudioInputDeviceCount());
				_audioDeviceNames = new string[numAudioDevices];
				for (int i = 0; i < numAudioDevices; i++)
				{
					_audioDeviceNames[i] = i.ToString("D2") + ") " + NativePlugin.GetAudioInputDeviceName(i).Replace("/", "_");
				}
			}

			_isInit = true;
		}

		private void OnEnable()
		{
			if (_icon == null)
			{
				_icon = Resources.Load<Texture2D>("AVProMovieCaptureIcon");
			}

			if (!_isCreated)
			{
				SetupWindow();
			}

			_isTrialVersion = IsTrialVersion();

			// Check that the plugin version number is not too old
			{
				string pluginVersionString = NativePlugin.GetPluginVersionString();
				_pluginVersionWarningText = string.Empty;
				if (!pluginVersionString.StartsWith(NativePlugin.ExpectedPluginVersion))
				{
					_pluginVersionWarningText = "Warning: Plugin version number " + pluginVersionString + " doesn't match the expected version number " + NativePlugin.ExpectedPluginVersion + ".  It looks like the plugin didn't upgrade correctly.  To resolve this please restart Unity and try to upgrade the package again.";
				}
			}
		}

		private void OnDisable()
		{
			SaveSettings();
			StopCapture();
			if (_gameObject != null)
			{
				DestroyImmediate(_gameObject);
				_gameObject = null;
				_capture = null;
				_captureScreen = null;
				_captureCamera = null;
				_captureCamera360 = null;
				_captureCamera360ODS = null;
				_cameraSelector = null;
				#if AVPRO_MOVIECAPTURE_PLAYABLES_SUPPORT
				_timelineController = null;
				#endif
			}
			_isInit = false;
			_isCreated = false;
			Repaint();
		}

		private void StartCapture()
		{
			_lastFileSize = 0;
			_lastEncodedSeconds = 0;
			_lastEncodedMinutes = 0;
			_lastEncodedFrame = 0;

			CreateComponents();
			if (_capture != null)
			{
				Configure(_capture);
				_capture.SelectVideoCodec();
				if (_capture.AudioCaptureSource != AudioCaptureSource.None)
				{
					_capture.SelectAudioCodec();
					_capture.SelectAudioInputDevice();
				}
				_capture.QueueStartCapture();
			}
		}

		private void StopCapture(bool cancelCapture = false)
		{
			if (_capture != null)
			{
				if (_capture.IsCapturing())
				{
					if (!cancelCapture)
					{
						_capture.StopCapture();
					}
					else
					{
						_capture.CancelCapture();
					}
				}
				_capture = null;
			}
		}

		// Updates 10 times/second
		void OnInspectorUpdate()
		{
			if (_capture != null)
			{
				if (Application.isPlaying)
				{
					if (_capture.IsCapturing())
					{
						_lastFileSize = _capture.GetCaptureFileSize();
					}

					if (!_capture.IsRealTime)
					{
						_lastEncodedSeconds = (uint)Mathf.FloorToInt((float)_capture.CaptureStats.NumEncodedFrames / _capture.FrameRate);
					}
					else
					{
						_lastEncodedSeconds = _capture.CaptureStats.TotalEncodedSeconds;
					}
					_lastEncodedMinutes = _lastEncodedSeconds / 60;
					_lastEncodedSeconds = _lastEncodedSeconds % 60;
					_lastEncodedFrame = _capture.CaptureStats.NumEncodedFrames % (uint)_capture.FrameRate;

					// If the capture has stopped automatically, we need to update the UI
					if (!_capture.IsPrepared() || (_capture.StopMode != StopMode.None && _capture.CaptureStats.NumEncodedFrames > 0 && !_capture.IsCapturing() && !_capture.IsStartCaptureQueued()))
					{
						StopCapture();
					}
				}
				else
				{
					StopCapture();
				}
			}
			else
			{
				if (_queueConfigureVideoCodec != null)
				{
					Codec tempCodec = _queueConfigureVideoCodec;
					_queueConfigureVideoCodec = null;
					tempCodec.ShowConfigWindow();
				}
				if (_queueConfigureAudioCodec != null)
				{
					Codec tempCodec = _queueConfigureAudioCodec;
					_queueConfigureAudioCodec = null;
					tempCodec.ShowConfigWindow();
				}

				if (_queueStart && Application.isPlaying)
				{
					_queueStart = false;
					StartCapture();
				}
			}

			Repaint();
		}

		private struct MediaApiItemMenuData
		{
			public MediaApiItemMenuData(IMediaApiItem item)
			{
				this.item = item;
			}

			public IMediaApiItem item;
		}

		private void MediaApiItemMenuCallback_Select(object obj)
		{
			if (((MediaApiItemMenuData)obj).item is Codec)
			{
				Codec codec = (Codec)((MediaApiItemMenuData)obj).item;

				if (codec.CodecType == CodecType.Video)
				{
					_videoCodec = codec;
				}
				else if (codec.CodecType == CodecType.Audio)
				{
					_audioCodec = codec;
				}
			}
			else if (((MediaApiItemMenuData)obj).item is Device)
			{
				Device device = (Device)((MediaApiItemMenuData)obj).item;

				if (device.DeviceType == DeviceType.AudioInput)
				{
					_audioInputDevice = device;
				}
			}
			UpdateSelectedCodec();
		}

		private GenericMenu CreateMediaItemMenu(System.Collections.IEnumerable items, IMediaApiItem selectedItem, IMediaApiItem matchMediaType)
		{
			GenericMenu menu = new GenericMenu();

#if UNITY_EDITOR_WIN
			MediaApi lastApi = MediaApi.Unknown;
#endif

			foreach (IMediaApiItem item in items)
			{				
				bool isEnabled = (matchMediaType == null || matchMediaType.MediaApi == item.MediaApi);
#if UNITY_EDITOR_WIN
				if (isEnabled && item.MediaApi != lastApi)
				{
					string title = string.Empty;
					switch (item.MediaApi)
					{
						case MediaApi.DirectShow:
						title = "DirectShow Legacy API:";
						break;
						case MediaApi.MediaFoundation:
						title = "Media Foundation API:";
						break;
					}
					menu.AddSeparator("");
					menu.AddDisabledItem(new GUIContent(title));
					lastApi = item.MediaApi;
				}
#endif
				if (isEnabled)
				{
					// Have to replace '/' as Unity context menu uses to this indicate a sub-menu
					string displayName = item.Name.Replace("/", "⁄");
					menu.AddItem(new GUIContent(displayName), item == selectedItem, MediaApiItemMenuCallback_Select, new MediaApiItemMenuData(item));
				}
				else
				{
					//menu.AddDisabledItem(new GUIContent(item.Name));
				}
			}

			return menu;
		}

		private bool ShowMediaItemList(string title, System.Collections.IEnumerable itemList, IMediaApiItem selectedItem, IMediaApiItem matchMediaType = null)
		{
			bool result = false;

			if (itemList == null || selectedItem == null)
			{
				return result;
			}

			if (!string.IsNullOrEmpty(title))
			{
				EditorGUILayout.LabelField(title, EditorStyles.boldLabel);
			}

			EditorGUILayout.BeginHorizontal();
			var rect = EditorGUILayout.GetControlRect(false);
			if (EditorGUI.DropdownButton(rect, new GUIContent(selectedItem.Name), FocusType.Keyboard))
			{
				CreateMediaItemMenu(itemList, selectedItem, matchMediaType).DropDown(rect);
			}

#if UNITY_EDITOR_WIN
			if (selectedItem is Codec)
			{
				EditorGUI.BeginDisabledGroup(!((Codec)selectedItem).HasConfigwindow);
				if (GUILayout.Button("Configure"))
				{
					result = true;
				}
				EditorGUI.EndDisabledGroup();
			}
#endif
			EditorGUILayout.EndHorizontal();

			return result;
		}

		private static bool ShowConfigList(string title, string[] items, bool[] isConfigurable, ref Codec codec, ref int itemIndex, ref bool itemChanged, bool showConfig = true, bool listEnabled = true)
		{
			bool result = false;

			if (itemIndex < 0 || items == null)
				return result;

			if (!string.IsNullOrEmpty(title))
			{
				EditorGUILayout.LabelField(title, EditorStyles.boldLabel);
			}
			EditorGUI.BeginDisabledGroup(!listEnabled);
			EditorGUILayout.BeginHorizontal();
			int newItemIndex = EditorGUILayout.Popup(itemIndex, items);
			itemChanged = (newItemIndex != itemIndex);
			itemIndex = newItemIndex;

#if UNITY_EDITOR_WIN
			if (showConfig && isConfigurable != null && itemIndex < isConfigurable.Length)
			{
				EditorGUI.BeginDisabledGroup(itemIndex == 0 || !isConfigurable[itemIndex]);
				if (GUILayout.Button("Configure"))
				{
					result = true;
				}
				EditorGUI.EndDisabledGroup();
			}
#endif
			EditorGUILayout.EndHorizontal();
			EditorGUI.EndDisabledGroup();

			return result;
		}

		void OnGUI()
		{
			if ((Application.platform != RuntimePlatform.WindowsEditor)
			&&  (Application.platform != RuntimePlatform.OSXEditor))
			{
				EditorGUILayout.LabelField("AVPro Movie Capture Window only works on the Windows and macOS platforms.");
				return;
			}

			if (!_isInit)
			{
				if (_isFailedInit)
				{
					GUILayout.Label("Error", EditorStyles.boldLabel);
					GUI.enabled = false;

					string missingDllMessage = string.Empty;
#if (UNITY_5 || UNITY_5_4_OR_NEWER)
					missingDllMessage = "Unity couldn't find the plugin DLL. Please select the native plugin files in 'Plugins/RenderHeads/AVProMovieCapture/Plugins' folder and select the correct platform in the Inspector.";
#else
					missingDllMessage = "Unity couldn't find the plugin DLL, Unity 4.x requires the 'Plugins' folder to be at the root of your project.  Please move the contents of the 'Plugins' folder (in Plugins/RenderHeads/AVProMovieCapture/Plugins) to the 'Plugins' folder in the root of your project.";
#endif

					GUILayout.TextArea(missingDllMessage);
					GUI.enabled = true;
					return;
				}
				else
				{
					EditorGUILayout.LabelField("Initialising...");
					return;
				}
			}

			if (!string.IsNullOrEmpty(_pluginVersionWarningText))
			{
				GUI.color = Color.yellow;
				GUILayout.TextArea(_pluginVersionWarningText);
				GUI.color = Color.white;
			}

			if (_so == null)
			{
				return;
			}

			_so.Update();

#if AVPRO_MOVIECAPTURE_GRAPHICSDEVICETYPE_51 && UNITY_EDITOR_WIN
			if (SystemInfo.graphicsDeviceType != UnityEngine.Rendering.GraphicsDeviceType.Direct3D11 &&
				SystemInfo.graphicsDeviceType != UnityEngine.Rendering.GraphicsDeviceType.Direct3D12)
			{
				GUI.color = Color.yellow;
				GUILayout.TextArea("Consider switching to D3D11 or D3D12 for best capture results.  You may need to change your Build platform to Windows.");
				GUI.color = Color.white;
			}
#endif

			if (_isTrialVersion)
			{
				EditorUtils.DrawSectionColored("- AVPRO MOVIE CAPTURE - TRIAL VERSION", ref _expandSectionTrial, DrawTrialMessage, Color.magenta, Color.magenta, Color.magenta);
				//EditorGUILayout.Space();
			}

			DrawControlButtonsGUI();
			EditorGUILayout.Space();

			// Live Capture Stats
			if (Application.isPlaying && _capture != null && (_capture.IsCapturing() || _capture.IsStartCaptureQueued()))
			{
				if (_propStopMode.enumValueIndex != (int)StopMode.None)
				{
					Rect r = GUILayoutUtility.GetRect(128f, EditorStyles.label.CalcHeight(GUIContent.none, 32f), GUILayout.ExpandWidth(true));
					float progress = _capture.GetProgress();
					EditorGUI.ProgressBar(r, progress, (progress * 100f).ToString("F1") + "%");
				}

				_scroll = EditorGUILayout.BeginScrollView(_scroll);
				DrawBaseCapturingGUI(_capture);
				DrawMoreCapturingGUI();
				EditorGUILayout.EndScrollView();
			}
			// Configuration
			else if (_capture == null)
			{
				string[] _toolNames = { "Settings", "Help" };
				_selectedTool = GUILayout.Toolbar(_selectedTool, _toolNames);
				switch (_selectedTool)
				{
					case 0:
						DrawConfigGUI_Toolbar();
						_scroll = EditorGUILayout.BeginScrollView(_scroll);
						DrawConfigGUI();
						EditorGUILayout.EndScrollView();
						break;
					case 1:
						_scroll = EditorGUILayout.BeginScrollView(_scroll);
						DrawConfigGUI_About();
						EditorGUILayout.EndScrollView();
						break;
				}
			}

			if (_so.ApplyModifiedProperties())
			{
				EditorUtility.SetDirty(this);
			}
		}

		private void DrawTrialMessage()
		{
			string message = "The free trial version is watermarked.  Upgrade to the full package to remove the watermark.";

			//GUI.backgroundColor = Color.yellow;
			//EditorGUILayout.BeginVertical(GUI.skin.box);
			//GUI.color = Color.yellow;
			//GUILayout.Label("AVPRO MOVIE CAPTURE - FREE TRIAL VERSION", EditorStyles.boldLabel);
			GUI.color = Color.white;
			GUILayout.Label(message, EditorStyles.wordWrappedLabel);
			if (GUILayout.Button("Upgrade Now"))
			{
				Application.OpenURL("https://www.assetstore.unity3d.com/en/#!/content/2670");
			}
			//EditorGUILayout.EndVertical();
			GUI.backgroundColor = Color.white;
			GUI.color = Color.white;
		}

		private void DrawControlButtonsGUI()
		{
			EditorGUILayout.BeginHorizontal();
			if (_capture == null)
			{
				GUI.backgroundColor = Color.green;
				string startString = "Start Capture";
				if (!IsCaptureRealTime())
				{
					startString = "Start Render";
				}
				if (GUILayout.Button(startString, GUILayout.Height(32f)))
				{
					bool isReady = true;
					if (_sourceType == SourceType.Camera &&
						_cameraNode == null &&
						_selectBy == CameraSelector.SelectByMode.Manual)
					{
						if ((ConfigTabs)_selectedConfigTab != ConfigTabs.Capture)
						{
							_cameraNode = Utils.GetUltimateRenderCamera();
						}
						if (_cameraNode == null)
						{
							Debug.LogError("[AVProMovieCapture] Please select a Camera to capture from, or select to capture from Screen.");
							isReady = false;
						}
					}

					if (isReady)
					{
						if (!Application.isPlaying)
						{
							EditorApplication.isPlaying = true;
							_queueStart = true;
						}
						else
						{
							StartCapture();
							Repaint();
						}
					}
				}
			}
			else
			{
				GUI.backgroundColor = Color.cyan;
				if (GUILayout.Button("Cancel", GUILayout.Height(32f)))
				{
					StopCapture(true);
					Repaint();
				}
				GUI.backgroundColor = Color.red;
				if (GUILayout.Button("Stop", GUILayout.Height(32f)))
				{
					StopCapture(false);
					Repaint();
				}

				if (_capture != null)
				{
					if (_capture.IsPaused())
					{
						GUI.backgroundColor = Color.green;
						if (GUILayout.Button("Resume", GUILayout.Height(32f)))
						{
							_capture.ResumeCapture();
							Repaint();
						}
					}
					else
					{
						GUI.backgroundColor = Color.yellow;
						if (GUILayout.Button("Pause", GUILayout.Height(32f)))
						{
							_capture.PauseCapture();
							Repaint();
						}
					}
				}
			}
			EditorGUILayout.EndHorizontal();

			GUI.backgroundColor = Color.white;

			GUILayout.BeginHorizontal();
			if (GUILayout.Button("Browse"))
			{
				if (!string.IsNullOrEmpty(CaptureBase.LastFileSaved))
				{
					Utils.ShowInExplorer(CaptureBase.LastFileSaved);
				}
			}
			{
				Color prevColor = GUI.color;
				GUI.color = Color.cyan;
				if (GUILayout.Button("View Last Capture"))
				{
					if (!string.IsNullOrEmpty(CaptureBase.LastFileSaved))
					{
						Utils.OpenInDefaultApp(CaptureBase.LastFileSaved);
					}
				}
				GUI.color = prevColor;
			}
			GUILayout.EndHorizontal();
		}


		public static void DrawBaseCapturingGUI(CaptureBase capture)
		{
			GUILayout.Space(8.0f);
			Texture texture = capture.GetPreviewTexture();
			if (texture != null)
			{
				float aspect = (float)texture.width / (float)texture.height;
				GUILayout.BeginHorizontal();

				//if (Event.current.type == EventType.Repaint)
				{
					if (_showAlpha)
					{
						Rect textureRect = GUILayoutUtility.GetAspectRect(aspect);//(width, width / aspect, GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(false));
						EditorGUI.DrawPreviewTexture(textureRect, texture, null, ScaleMode.ScaleToFit);
						textureRect = GUILayoutUtility.GetAspectRect(aspect);//width, width / aspect, GUILayout.ExpandWidth(false), GUILayout.ExpandHeight(false)); ;
						EditorGUI.DrawTextureAlpha(textureRect, texture, ScaleMode.ScaleToFit);
					}
					else
					{
						Rect textureRect = GUILayoutUtility.GetAspectRect(aspect, GUILayout.MaxHeight(256f));//width, width / aspect, GUILayout.ExpandWidth(false), GUILayout.ExpandHeight(false));
						EditorGUI.DrawPreviewTexture(textureRect, texture, null, ScaleMode.ScaleToFit);
					}
				}
				GUILayout.EndHorizontal();

				GUILayout.BeginHorizontal();
				GUILayout.FlexibleSpace();
				_showAlpha = GUILayout.Toggle(_showAlpha, "Show Alpha", GUILayout.ExpandWidth(false));
				GUILayout.FlexibleSpace();
				GUILayout.EndHorizontal();
				GUILayout.Space(8.0f);
			}

			GUILayout.Label("Output", EditorStyles.boldLabel);
			EditorGUILayout.BeginVertical("box");
			EditorGUI.indentLevel++;

			GUILayout.Label("Recording to: " + System.IO.Path.GetFileName(capture.LastFilePath), EditorStyles.wordWrappedLabel);
			GUILayout.Space(8.0f);

			GUILayout.Label("Video");
			EditorGUILayout.LabelField("Dimensions", capture.GetRecordingWidth() + "x" + capture.GetRecordingHeight() + " @ " + capture.FrameRate.ToString("F2") + "hz");
			if (capture.OutputTarget == OutputTarget.VideoFile)
			{
				EditorGUILayout.LabelField("Codec", (capture.SelectedVideoCodec != null)?capture.SelectedVideoCodec.Name:"None");
			}
			else if (capture.OutputTarget == OutputTarget.ImageSequence)
			{
				EditorGUILayout.LabelField("Codec", capture.NativeImageSequenceFormat.ToString());
			}

			if (capture.AudioCaptureSource != AudioCaptureSource.None)
			{
				GUILayout.Label("Audio");
				if (capture.AudioCaptureSource == AudioCaptureSource.Unity)
				{
					EditorGUILayout.LabelField("Source", "Unity");
				}
				else if (capture.AudioCaptureSource == AudioCaptureSource.Microphone)
				{
					EditorGUILayout.LabelField("Source", (capture.SelectedAudioInputDevice != null)?capture.SelectedAudioInputDevice.Name:"None");
				}
				EditorGUILayout.LabelField("Codec", (capture.SelectedAudioCodec!= null)?capture.SelectedAudioCodec.Name:"None");
				if (capture.AudioCaptureSource == AudioCaptureSource.Unity)
				{
					EditorGUILayout.LabelField("Sample Rate", (capture.CaptureStats.UnityAudioSampleRate/1000f).ToString("F1") + "Khz");
					EditorGUILayout.LabelField("Channels", capture.CaptureStats.UnityAudioChannelCount.ToString());
				}
			}

			EditorGUI.indentLevel--;
			EditorGUILayout.EndVertical();

			GUILayout.Space(8.0f);

			GUILayout.Label("Stats", EditorStyles.boldLabel);
			EditorGUILayout.BeginVertical("box");
			EditorGUI.indentLevel++;

			if (capture.CaptureStats.FPS > 0f)
			{
				Color originalColor = GUI.color;
				if (capture.IsRealTime)
				{
					float fpsDelta = (capture.CaptureStats.FPS - capture.FrameRate);
					GUI.color = Color.red;
					if (fpsDelta > -10)
					{
						GUI.color = Color.yellow;
					}
					if (fpsDelta > -2)
					{
						GUI.color = Color.green;
					}
				}

				EditorGUILayout.LabelField("Capture Rate", string.Format("{0:0.##} / {1:F2} FPS", capture.CaptureStats.FPS, capture.FrameRate));

				GUI.color = originalColor;
			}
			else
			{
				EditorGUILayout.LabelField("Capture Rate", string.Format(".. / {0:F2} FPS", capture.FrameRate));
			}

			EditorGUILayout.LabelField("Encoded Frames", capture.CaptureStats.NumEncodedFrames.ToString());

			EditorGUI.indentLevel--;
			EditorGUILayout.EndVertical();
		}

		public void DrawMoreCapturingGUI()
		{
			GUILayout.Label("More Stats", EditorStyles.boldLabel);
			EditorGUILayout.BeginVertical("box");
			EditorGUI.indentLevel++;

			EditorGUILayout.LabelField("File Size", ((float)_lastFileSize / (1024f * 1024f)).ToString("F1") + "MB");
			EditorGUILayout.LabelField("Video Length", _lastEncodedMinutes.ToString("00") + ":" + _lastEncodedSeconds.ToString("00") + "." + _lastEncodedFrame.ToString("000"));

			EditorGUILayout.PrefixLabel("Dropped Frames");
			EditorGUI.indentLevel++;
			EditorGUILayout.LabelField("In Unity", _capture.CaptureStats.NumDroppedFrames.ToString());
			EditorGUILayout.LabelField("In Encoder", _capture.CaptureStats.NumDroppedEncoderFrames.ToString());
			EditorGUI.indentLevel--;

			if (IsAudioCaptured())
			{
				if (_capture.AudioCaptureSource == AudioCaptureSource.Unity && _capture.UnityAudioCapture != null)
				{
					EditorGUILayout.LabelField("Audio Overflows", _capture.UnityAudioCapture.OverflowCount.ToString());
				}
			}

			EditorGUI.indentLevel--;
			EditorGUILayout.EndVertical();
		}

		private void DrawConfigGUI_Toolbar()
		{
			_selectedConfigTab = GUILayout.Toolbar(_selectedConfigTab, _tabNames);
		}

		private void DrawConfigGUI()
		{
			switch ((ConfigTabs)_selectedConfigTab)
			{
				case ConfigTabs.Encoding:
					DrawConfigGUI_Encoding();
					break;
				case ConfigTabs.Capture:
					DrawConfigGUI_Capture();
					break;
				case ConfigTabs.Visual:
					DrawConfigGUI_Visual();
					break;
				case ConfigTabs.Audio:
					DrawConfigGUI_Audio();
					break;
			}

			GUILayout.FlexibleSpace();
		}

		public static void DrawConfigGUI_About()
		{
			string version = NativePlugin.GetPluginVersionString();

			EditorGUILayout.BeginHorizontal();
			GUILayout.FlexibleSpace();

			if (_icon == null)
			{
				_icon = Resources.Load<Texture2D>("AVProMovieCaptureIcon");
			}
			if (_icon != null)
			{
				GUILayout.Label(new GUIContent(_icon));
			}
			GUILayout.FlexibleSpace();
			EditorGUILayout.EndHorizontal();

			EditorGUILayout.BeginHorizontal();
			GUILayout.FlexibleSpace();
			GUI.color = Color.yellow;
			GUILayout.Label("AVPro Movie Capture by RenderHeads Ltd", EditorStyles.boldLabel);
			GUI.color = Color.white;
			GUILayout.FlexibleSpace();
			EditorGUILayout.EndHorizontal();

			EditorGUILayout.BeginHorizontal();
			GUILayout.FlexibleSpace();
			GUI.color = Color.yellow;
			GUILayout.Label("version " + version + " (scripts v" + NativePlugin.ScriptVersion + ")");
			GUI.color = Color.white;
			GUILayout.FlexibleSpace();
			EditorGUILayout.EndHorizontal();

			// Links
			{
				GUILayout.Space(32f);
				GUI.backgroundColor = Color.white;

				EditorGUILayout.LabelField("AVPro Movie Capture Links", EditorStyles.boldLabel);

				GUILayout.Space(8f);

				EditorGUILayout.LabelField("Documentation");
				if (GUILayout.Button("User Manual", GUILayout.ExpandWidth(false)))
				{
					Application.OpenURL(LinkUserManual);
				}

				GUILayout.Space(16f);

				GUILayout.Label("Bugs and Support");
				if (GUILayout.Button("GitHub Issues", GUILayout.ExpandWidth(false)))
				{
					Application.OpenURL(LinkSupport);
				}

				GUILayout.Space(16f);

				GUILayout.Label("Rate and Review (★★★★☆)", GUILayout.ExpandWidth(false));
				if (GUILayout.Button("Asset Store Page", GUILayout.ExpandWidth(false)))
				{
					Application.OpenURL(LinkAssetStorePage);
				}

				GUILayout.Space(16f);

				GUILayout.Label("Community");
				if (GUILayout.Button("Forum Thread", GUILayout.ExpandWidth(false)))
				{
					Application.OpenURL(LinkForumPage);
				}

				GUILayout.Space(16f);

				GUILayout.Label("Website", GUILayout.ExpandWidth(false));
				if (GUILayout.Button("Official Website", GUILayout.ExpandWidth(false)))
				{
					Application.OpenURL(LinkPluginWebsite);
				}
			}

			// Credits
			{
				GUILayout.Space(32f);
				EditorGUILayout.LabelField("Credits", EditorStyles.boldLabel);
				GUILayout.Space(8f);
				EditorUtils.CentreLabel("Programming", EditorStyles.boldLabel);
				EditorUtils.CentreLabel("Andrew Griffiths");
				EditorUtils.CentreLabel("Morris Butler");
				EditorUtils.CentreLabel("Richard Turnbull");
				EditorUtils.CentreLabel("Sunrise Wang");
				GUILayout.Space(8f);
				EditorUtils.CentreLabel("Graphics", EditorStyles.boldLabel);
				EditorUtils.CentreLabel("Jeff Rusch");
				EditorUtils.CentreLabel("Luke Godward");
			}

			// Bug reporting
			{
				GUILayout.Space(32f);

				EditorGUILayout.LabelField("Bug Reporting Notes", EditorStyles.boldLabel);

				EditorGUILayout.SelectableLabel(SupportMessage, EditorStyles.wordWrappedLabel, GUILayout.Height(180f));
			}
		}

		private void DrawConfigGUI_Capture()
		{
			//GUILayout.Label("Capture", EditorStyles.boldLabel);
			EditorGUILayout.BeginVertical("box");
			//EditorGUI.indentLevel++;

			// Time
			{
				GUILayout.Space(8f);
				EditorGUILayout.LabelField("Time", EditorStyles.boldLabel);
				EditorGUI.indentLevel++;
				_captureModeIndex = EditorGUILayout.Popup("Capture Mode", _captureModeIndex, _captureModes);
				GUILayout.BeginHorizontal();
				EditorGUILayout.PropertyField(_propFrameRate, GUILayout.ExpandWidth(false));
				_propFrameRate.floatValue = Mathf.Clamp(_propFrameRate.floatValue, 0.01f, 240f);
				EditorUtils.FloatAsPopup("▶", "Common Frame Rates", _so, _propFrameRate, EditorUtils.CommonFrameRateNames, EditorUtils.CommonFrameRateValues);
				GUILayout.EndHorizontal();
				if (IsCaptureRealTime())
				{
					EditorGUILayout.PropertyField(_propTimelapseScale);
					_propTimelapseScale.intValue = Mathf.Max(1, _propTimelapseScale.intValue);
				}
				EditorGUI.indentLevel--;
			}

			// Source
			GUILayout.Space(8f);
			EditorGUILayout.LabelField("Source", EditorStyles.boldLabel);
			EditorGUI.indentLevel++;
			EditorUtils.EnumAsDropdown("Source", _propSourceType, _sourceNames);

			if (_sourceType == SourceType.Camera360ODS && IsCaptureRealTime())
			{
				GUI.color = Color.yellow;
				GUILayout.TextArea("Warning: This source type is very slow and not suitable for 'Realtime Capture'.  Consider changing the capture mode to 'Offline Render'.");
				GUI.color = Color.white;
			}

			EditorGUI.indentLevel--;

			//_sourceType = (SourceType)EditorGUILayout.EnumPopup("Type", _sourceType);
			if (_sourceType == SourceType.Camera || _sourceType == SourceType.Camera360 || _sourceType == SourceType.Camera360ODS)
			{
				GUILayout.Space(8f);
				EditorGUILayout.LabelField("Camera Selector", EditorStyles.boldLabel);
				EditorGUI.indentLevel++;

				EditorGUILayout.PropertyField(_propCameraSelectorSelectBy);
				if (_propCameraSelectorSelectBy.enumValueIndex == (int)CameraSelector.SelectByMode.Name)
				{
					EditorGUILayout.PropertyField(_propCameraSelectorName, _guiCameraSelectorName);

				}
				else if (_propCameraSelectorSelectBy.enumValueIndex == (int)CameraSelector.SelectByMode.Tag)
				{
					EditorGUILayout.PropertyField(_propCameraSelectorTag, _guiCameraSelectorTag);
				}
				else if (_propCameraSelectorSelectBy.enumValueIndex == (int)CameraSelector.SelectByMode.Manual)
				{
					if (_cameraNode == null)
					{
						_cameraNode = Utils.GetUltimateRenderCamera();
					}
					_cameraNode = (Camera)EditorGUILayout.ObjectField("Camera", _cameraNode, typeof(Camera), true);
				}
#if !SUPPORT_SCENE_VIEW_GIZMOS_CAPTURE
				else if (_propCameraSelectorSelectBy.enumValueIndex == (int)CameraSelector.SelectByMode.EditorSceneView)
				{
					GUI.color = Color.yellow;
					GUILayout.TextArea("Warning: Scene View capture only currently supports gizmo capture up to Unity 2018.2.x");
					GUI.color = Color.white;
				}
#endif
				if (_sourceType == SourceType.Camera && _cameraNode != null)
				{
					EditorGUILayout.PropertyField(_propUseContributingCameras, _guiContributingCameras);
				}

				EditorGUILayout.PropertyField(_propCameraSelectorScanFrequency);
				EditorGUILayout.PropertyField(_propCameraSelectorScanHiddenCameras);

				EditorGUI.indentLevel--;
			}


			// Screen options
			if (_sourceType == SourceType.Screen)
			{
				GUILayout.Space(8f);
				EditorGUILayout.LabelField("Cursor", EditorStyles.boldLabel);
				EditorGUI.indentLevel++;
				_captureMouseCursor = EditorGUILayout.Toggle("Capture Mouse Cursor", _captureMouseCursor);
				_mouseCursorTexture = (Texture2D)EditorGUILayout.ObjectField("Mouse Cursor Texture", _mouseCursorTexture, typeof(Texture2D), false);
				EditorGUI.indentLevel--;
			}

			// Camera overrides
			if (_sourceType == SourceType.Camera || _sourceType == SourceType.Camera360 || _sourceType == SourceType.Camera360ODS)
			{
				GUILayout.Space(8f);
				EditorGUILayout.LabelField("Camera Overrides", EditorStyles.boldLabel);
				EditorGUI.indentLevel++;

				{
					EditorUtils.EnumAsDropdown("Resolution", _propRenderResolution, CaptureBaseEditor.ResolutionStrings);
				}

				if (_renderResolution == CaptureBase.Resolution.Custom)
				{
					_renderSize = EditorGUILayout.Vector2Field("Size", _renderSize);
					_renderSize = new Vector2(Mathf.Clamp((int)_renderSize.x, 1, NativePlugin.MaxRenderWidth), Mathf.Clamp((int)_renderSize.y, 1, NativePlugin.MaxRenderHeight));
				}

				{
					string currentAA = "None";
					if (QualitySettings.antiAliasing > 1)
					{
						currentAA = QualitySettings.antiAliasing.ToString() + "x";
					}
					EditorUtils.IntAsDropdown("Anti-aliasing", _propRenderAntiAliasing, new string[] { "Current (" + currentAA + ")", "None", "2x", "4x", "8x" }, new int[] { -1, 1, 2, 4, 8 });
				}

				if (_cameraNode != null)
				{
					if (_cameraNode.actualRenderingPath == RenderingPath.DeferredLighting
#if AVPRO_MOVIECAPTURE_DEFERREDSHADING
					|| _cameraNode.actualRenderingPath == RenderingPath.DeferredShading
#endif
					)
					{
						GUI.color = Color.yellow;
						GUILayout.TextArea("Warning: Antialiasing by MSAA is not supported as camera is using deferred rendering path");
						GUI.color = Color.white;
						_renderAntiAliasing = -1;
					}
					if (_cameraNode.clearFlags == CameraClearFlags.Nothing || _cameraNode.clearFlags == CameraClearFlags.Depth)
					{
						if (_renderResolution != CaptureBase.Resolution.Original || _renderAntiAliasing != -1)
						{
							GUI.color = Color.yellow;
							GUILayout.TextArea("Warning: Overriding camera resolution or anti-aliasing when clear flag is set to " + _cameraNode.clearFlags + " may result in incorrect captures");
							GUI.color = Color.white;
						}
					}
				}

				EditorGUI.indentLevel--;
			}

			// 360 Cubemap
			if (_sourceType == SourceType.Camera360)
			{
				GUILayout.Space(8f);
				EditorGUILayout.LabelField("360 Camera", EditorStyles.boldLabel);
				EditorGUI.indentLevel++;
				EditorGUILayout.PropertyField(_propRender180Degrees);
				{
					CaptureBase.CubemapResolution cubemapEnum = (CaptureBase.CubemapResolution)_cubemapResolution;
					_cubemapResolution = (int)((CaptureBase.CubemapResolution)EditorGUILayout.EnumPopup("Resolution", cubemapEnum));
				}
				{
					CaptureBase.CubemapDepth depthEnum = (CaptureBase.CubemapDepth)_cubemapDepth;
					_cubemapDepth = (int)((CaptureBase.CubemapDepth)EditorGUILayout.EnumPopup("Depth", depthEnum));
				}
				{
					StereoPacking stereoEnum = (StereoPacking)_cubemapStereoPacking;
					_cubemapStereoPacking = (int)((StereoPacking)EditorGUILayout.EnumPopup("Stereo Mode", stereoEnum));
				}
				if (_cubemapStereoPacking != (int)StereoPacking.None)
				{
					#if AVPRO_MOVIECAPTURE_UNITY_STEREOCUBEMAP_RENDER
					if (!PlayerSettings.enable360StereoCapture)
					{
						GUI.color = Color.yellow;
						GUILayout.TextArea("Warning: 360 Stereo Capture needs to be enabled in PlayerSettings");
						GUI.color = Color.white;
						if (GUILayout.Button("Enable 360 Stereo Capture"))
						{
							PlayerSettings.enable360StereoCapture = true;
						}
					}
					#endif
					_cubemapStereoIPD = EditorGUILayout.FloatField("Interpupillary distance", _cubemapStereoIPD);
				}
				EditorGUILayout.PropertyField(_propCaptureWorldSpaceGUI, _guiCaptureWorldSpaceUI);
				EditorGUILayout.PropertyField(_propSupportCameraRotation, _guiCameraRotation);

				if (_propSupportCameraRotation.boolValue)
				{
					EditorGUILayout.PropertyField(_propOnlyLeftRightRotation);
				}
				EditorGUI.indentLevel--;
			}

			// 360 Cubemap ODS
			if (_sourceType == SourceType.Camera360ODS)
			{
				GUILayout.Space(8f);
				EditorGUI.indentLevel++;
				EditorGUILayout.LabelField("Source Options", EditorStyles.boldLabel);
				EditorGUI.indentLevel++;
				{
					EditorGUILayout.PropertyField(_propOdsRender180Degrees);
					EditorGUILayout.PropertyField(_propOdsIPD, _guiInterpupillaryDistance);
					EditorGUILayout.PropertyField(_propOdsPixelSliceSize);
					EditorGUILayout.PropertyField(_propOdsPaddingSize);
					EditorGUILayout.PropertyField(_propOdsCameraClearMode);
					EditorGUILayout.PropertyField(_propOdsCameraClearColor);
				}
				EditorGUI.indentLevel--;
				EditorGUI.indentLevel--;
			}

			// Start / Stop
			{
				GUILayout.Space(8f);
				EditorGUILayout.LabelField("Start / Stop", EditorStyles.boldLabel);
				EditorGUI.indentLevel++;

				EditorGUILayout.PropertyField(_propStartDelay, _guiStartDelay);

				if ((StartDelayMode)_propStartDelay.enumValueIndex == StartDelayMode.RealSeconds ||
					(StartDelayMode)_propStartDelay.enumValueIndex == StartDelayMode.GameSeconds)
				{
					EditorGUILayout.PropertyField(_propStartDelaySeconds, _guiSeconds);
				}

				EditorGUILayout.Separator();

				_stopMode = (StopMode)EditorGUILayout.EnumPopup("Stop Mode", _stopMode);
				if (_stopMode == StopMode.FramesEncoded)
				{
					_stopFrames = EditorGUILayout.IntField("Frames", _stopFrames);
				}
				else if (_stopMode == StopMode.SecondsElapsed || _stopMode == StopMode.SecondsEncoded)
				{
					_stopSeconds = EditorGUILayout.FloatField("Seconds", _stopSeconds);
				}
				EditorGUI.indentLevel--;
			}

			GUILayout.Space(8f);
			if (GUILayout.Button("Reset All Settings"))
			{
				ResetSettings();
			}
			GUILayout.Space(4f);

			//EditorGUI.indentLevel--;
			EditorGUILayout.EndVertical();
		}

		private void DrawConfigGUI_Encoding()
		{
			//GUILayout.Label("Target", EditorStyles.boldLabel);
			EditorGUILayout.BeginVertical("box");
			//EditorGUI.indentLevel++;

			EditorUtils.EnumAsDropdown("Output Target", _propOutputTarget, EditorUtils.OutputTargetNames);

			GUILayout.Space(8f);
			if (_propOutputTarget.enumValueIndex == (int)OutputTarget.VideoFile ||
				_propOutputTarget.enumValueIndex == (int)OutputTarget.ImageSequence)
			{
				bool isImageSequence = (_propOutputTarget.enumValueIndex == (int)OutputTarget.ImageSequence);

				if (isImageSequence)
				{
					EditorUtils.EnumAsDropdown("Format", _propImageSequenceFormat, Utils.GetNativeImageSequenceFormatNames());
					GUILayout.Space(8f);
				}

				// File path
				EditorGUILayout.LabelField("File Path", EditorStyles.boldLabel);
				EditorGUI.indentLevel++;
				_outputFolderIndex = EditorGUILayout.Popup("Relative to", _outputFolderIndex, _outputFolders);
				if (_outputFolderIndex != (int)CaptureBase.OutputPath.Absolute)
				{
					_outputFolderRelative = EditorGUILayout.TextField("SubFolder(s)", _outputFolderRelative);
				}
				else
				{
					EditorGUILayout.BeginHorizontal();
					_outputFolderAbsolute = EditorGUILayout.TextField("Path", _outputFolderAbsolute);
					if (GUILayout.Button(">", GUILayout.Width(22)))
					{
						_outputFolderAbsolute = EditorUtility.SaveFolderPanel("Select Folder To Store Video Captures", System.IO.Path.GetFullPath(System.IO.Path.Combine(Application.dataPath, "../")), "");
						EditorUtility.SetDirty(this);
					}
					EditorGUILayout.EndHorizontal();
				}
				EditorGUI.indentLevel--;

				GUILayout.Space(8f);

				// File name
				EditorGUILayout.LabelField("File Name", EditorStyles.boldLabel);
				EditorGUI.indentLevel++;
				_filenamePrefixFromSceneName = EditorGUILayout.Toggle("From Scene Name", _filenamePrefixFromSceneName);
				if (_filenamePrefixFromSceneName)
				{
#if AVPRO_MOVIECAPTURE_SCENEMANAGER_53
					string currentScenePath = UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene().path;
#else
					string currentScenePath = EditorApplication.currentScene;
#endif
					_filenamePrefix = System.IO.Path.GetFileNameWithoutExtension(currentScenePath);
					if (string.IsNullOrEmpty(_filenamePrefix))
					{
						_filenamePrefix = "capture";
					}
				}
				EditorGUI.BeginDisabledGroup(_filenamePrefixFromSceneName);
				_filenamePrefix = EditorGUILayout.TextField("Prefix", _filenamePrefix);
				EditorGUI.EndDisabledGroup();
				if (!isImageSequence)
				{
					_appendTimestamp = EditorGUILayout.Toggle("Append Timestamp", _appendTimestamp);
				}
				else
				{
					EditorGUILayout.PropertyField(_propImageSequenceStartFrame, _guiStartFrame);
					EditorGUILayout.PropertyField(_propImageSequenceZeroDigits, _guiZeroDigits);
				}
				EditorGUI.indentLevel--;
				GUILayout.Space(8f);

				// File container
				if (!isImageSequence)
				{
					EditorGUILayout.LabelField("File Container", EditorStyles.boldLabel);
					EditorGUI.indentLevel++;
					_fileContainerIndex = EditorGUILayout.Popup("Extension", _fileContainerIndex, _fileExtensions);
					if (_fileContainerIndex >= 0  && _fileContainerIndex < _fileExtensions.Length)
					{
						_filenameExtension = _fileExtensions[_fileContainerIndex].ToLower();
					}
					EditorGUI.indentLevel--;
				}
			}
			else if (_propOutputTarget.enumValueIndex == (int)OutputTarget.NamedPipe)
			{
				EditorGUILayout.PropertyField(_propNamedPipePath);
			}

			DrawVisualCodecList();
			DrawAudioCodecList();

			if (_propOutputTarget.enumValueIndex == (int)OutputTarget.VideoFile ||
				_propOutputTarget.enumValueIndex == (int)OutputTarget.ImageSequence)
			{
				GUILayout.Space(8f);
				EditorGUILayout.LabelField("Encoder Hints", EditorStyles.boldLabel);
				EditorGUI.indentLevel++;
				if (_propOutputTarget.enumValueIndex == (int)OutputTarget.VideoFile)
				{
					EditorUtils.BitrateField("Average Bitrate", _propVideoHintsAverageBitrate);
#if UNITY_EDITOR_WIN
					EditorUtils.BitrateField("Maxiumum Bitrate", _propVideoHintsMaximumBitrate);

					// Ensure that the maximum value is larger than the average value, or zero
					if (_propVideoHintsMaximumBitrate.intValue != 0)
					{
						_propVideoHintsMaximumBitrate.intValue = Mathf.Max(_propVideoHintsMaximumBitrate.intValue, _propVideoHintsAverageBitrate.intValue);
					}
#endif
					EditorGUILayout.PropertyField(_propVideoHintsQuality);
					EditorGUILayout.PropertyField(_propVideoHintsKeyframeInterval);

					EditorGUILayout.PropertyField(_propVideoHintsAllowFastStart);
					EditorGUILayout.PropertyField(_propVideoHintsSupportTransparency);

#if UNITY_EDITOR_WIN
					EditorGUILayout.PropertyField(_propVideoHintsUseHardwareEncoding, new GUIContent("Hardware Encoding"));
#endif
				}
				else if (_propOutputTarget.enumValueIndex == (int)OutputTarget.ImageSequence)
				{
#if UNITY_EDITOR_OSX
					EditorGUILayout.PropertyField(_propImageHintsQuality);
#endif
					EditorGUILayout.PropertyField(_propImageHintsSupportTransparency);
				}
				EditorGUI.indentLevel--;
			}

			//EditorGUI.indentLevel--;
			EditorGUILayout.EndVertical();
		}

		private void DrawConfigGUI_Visual()
		{
			//GUILayout.Label("Video", EditorStyles.boldLabel);
			EditorGUILayout.BeginVertical("box");
			//EditorGUI.indentLevel++;

			{
				Vector2 outSize = Vector2.zero;
				if (_sourceType == SourceType.Screen)
				{
					// We can't just use Screen.width and Screen.height because Unity returns the size of this window
					// So instead we look for a camera with no texture target and a valid viewport
					int inWidth = 1;
					int inHeight = 1;
					foreach (Camera cam in Camera.allCameras)
					{
						if (cam.targetTexture == null)
						{
							float rectWidth = Mathf.Clamp01(cam.rect.width + cam.rect.x) - Mathf.Clamp01(cam.rect.x);
							float rectHeight = Mathf.Clamp01(cam.rect.height + cam.rect.y) - Mathf.Clamp01(cam.rect.y);
							if (rectWidth > 0.0f && rectHeight > 0.0f)
							{
								inWidth = Mathf.FloorToInt(cam.pixelWidth / rectWidth);
								inHeight = Mathf.FloorToInt(cam.pixelHeight / rectHeight);
								//Debug.Log (rectWidth + "    " + (cam.rect.height - cam.rect.y) + " " + cam.pixelHeight + " = " + inWidth + "x" + inHeight);
								break;
							}
						}
					}
					outSize = CaptureBase.GetRecordingResolution(inWidth, inHeight, GetDownScaleFromIndex(_downScaleIndex), new Vector2(_downscaleX, _downscaleY));
				}
				else
				{
					if (_cameraNode != null)
					{
						int inWidth = Mathf.FloorToInt(_cameraNode.pixelRect.width);
						int inHeight = Mathf.FloorToInt(_cameraNode.pixelRect.height);

						if (_renderResolution != CaptureBase.Resolution.Original)
						{
							float rectWidth = Mathf.Clamp01(_cameraNode.rect.width + _cameraNode.rect.x) - Mathf.Clamp01(_cameraNode.rect.x);
							float rectHeight = Mathf.Clamp01(_cameraNode.rect.height + _cameraNode.rect.y) - Mathf.Clamp01(_cameraNode.rect.y);

							if (_renderResolution == CaptureBase.Resolution.Custom)
							{
								inWidth = (int)_renderSize.x;
								inHeight = (int)_renderSize.y;
							}
							else
							{
								CaptureBase.GetResolution(_renderResolution, ref inWidth, ref inHeight);
								inWidth = Mathf.FloorToInt(inWidth * rectWidth);
								inHeight = Mathf.FloorToInt(inHeight * rectHeight);
							}
						}

						outSize = CaptureBase.GetRecordingResolution(inWidth, inHeight, GetDownScaleFromIndex(_downScaleIndex), new Vector2(_downscaleX, _downscaleY));
					}
				}

				GUILayout.Space(8f);
				GUILayout.BeginHorizontal();
				GUILayout.FlexibleSpace();
				GUI.color = Color.cyan;
				GUILayout.TextArea("Output: " + (int)outSize.x + " x " + (int)outSize.y + " @ " + _frameRate.ToString("F2"));
				GUILayout.FlexibleSpace();
				GUILayout.EndHorizontal();
				GUILayout.Space(8f);
				GUI.color = Color.white;

			}

			_downScaleIndex = EditorGUILayout.Popup("Down Scale", _downScaleIndex, _downScales);
			if (_downScaleIndex == 5)
			{
				Vector2 maxVideoSize = new Vector2(_downscaleX, _downscaleY);
				maxVideoSize = EditorGUILayout.Vector2Field("Size", maxVideoSize);
				_downscaleX = Mathf.Clamp((int)maxVideoSize.x, 1, NativePlugin.MaxRenderWidth);
				_downscaleY = Mathf.Clamp((int)maxVideoSize.y, 1, NativePlugin.MaxRenderHeight);
			}

			GUILayout.Space(8f);
			//EditorGUILayout.LabelField("Codec", EditorStyles.boldLabel);

			DrawConfigGUI_MotionBlur();

			//EditorGUI.indentLevel--;
			EditorGUILayout.EndVertical();
		}

		private void UpdateSelectedCodec()
		{
			// Assign the first codec if none is selected
			if (_videoCodec == null && CodecManager.VideoCodecs.Count > 0)
			{
				_videoCodec = CodecManager.VideoCodecs.Codecs[0];
			}
			if (_videoCodec != null)
			{
				// Ensure the audio codec uses the same API as the video codec,
				// otherwise assign the first audio codec
				if (_audioCodec == null || (_audioCodec != null && _audioCodec.MediaApi != _videoCodec.MediaApi))
				{
					_audioCodec = CodecManager.AudioCodecs.GetFirstWithMediaApi(_videoCodec.MediaApi);
				}
				if (_audioInputDevice == null || (_audioInputDevice != null && _audioInputDevice.MediaApi != _videoCodec.MediaApi))
				{
					_audioInputDevice = DeviceManager.AudioInputDevices.GetFirstWithMediaApi(_videoCodec.MediaApi);
				}
			}

			// Select the appropriate file extension based on the selected codecs
			UpdateFileExtension();
		}

		private void UpdateFileExtension()
		{
			// There are cases where the plugin is deinitalised, so it needs to be inited to be able to query the file extension
			if (NativePlugin.GetVideoCodecCount() <= 0)
			{
				NativePlugin.Init();
			}

			_fileExtensions = GetSuitableFileExtensions(_videoCodec, IsAudioCaptured()?_audioCodec:null);
			if (_fileContainerIndex >= _fileExtensions.Length)
			{
				_fileContainerIndex = 0;
			}
			if (_fileContainerIndex < _fileExtensions.Length)
			{
				_filenameExtension = _fileExtensions[_fileContainerIndex].ToLower();
			}
		}

		private void DrawVisualCodecList()
		{
			GUILayout.Space(8f);
			if (_outputTarget == OutputTarget.VideoFile)
			{
				if (_videoCodec == null && CodecManager.VideoCodecs.Count > 0)
				{
					_videoCodec = CodecManager.VideoCodecs.Codecs[0];
				}
				if (_audioCodec == null && CodecManager.AudioCodecs.Count > 0)
				{
					_audioCodec = CodecManager.AudioCodecs.Codecs[0];
				}
				if (ShowMediaItemList("Video Codec", CodecManager.VideoCodecs, _videoCodec, null))
				{
					_queueConfigureVideoCodec = _videoCodec;
				}

				if (_videoCodec != null)
				{
#if UNITY_EDITOR_WIN
					if (_videoCodec.MediaApi == MediaApi.DirectShow)
					{
						if (_videoCodec.Name.EndsWith("Cinepak Codec by Radius")
								|| _videoCodec.Name.EndsWith("DV Video Encoder")
								|| _videoCodec.Name.EndsWith("Microsoft Video 1")
								|| _videoCodec.Name.EndsWith("Microsoft RLE")
								|| _videoCodec.Name.EndsWith("Logitech Video (I420)")
								|| _videoCodec.Name.EndsWith("Intel IYUV codec")
								)
						{
							GUI.color = Color.yellow;
							GUILayout.TextArea("Warning: Legacy codec, not recommended");
							GUI.color = Color.white;
						}
						if (_videoCodec.Name.Contains("Decoder") || _videoCodec.Name.Contains("Decompressor"))
						{
							GUI.color = Color.yellow;
							GUILayout.TextArea("Warning: Codec may contain decompressor only");
							GUI.color = Color.white;
						}
						if (CodecManager.VideoCodecs.Count < 6)
						{
							GUI.color = Color.cyan;
							GUILayout.TextArea("Low number of codecs, consider installing more");
							GUI.color = Color.white;
						}
					}
#endif
					if (_videoCodec.Name.EndsWith("Uncompressed"))
					{
						GUI.color = Color.yellow;
						GUILayout.TextArea("Warning: Uncompressed may result in very large files");
						GUI.color = Color.white;
					}
				}
			}
		}

		private bool IsCaptureRealTime()
		{
			return (_captureModeIndex == 0);
		}

		private bool IsAudioCaptured()
		{
			return (_outputTarget == OutputTarget.VideoFile && _audioCaptureSource != AudioCaptureSource.None && (IsCaptureRealTime() || _audioCaptureSource != AudioCaptureSource.Manual));
		}

		private void DrawAudioCodecList()
		{
			if (_outputTarget != OutputTarget.VideoFile)
			{
				return;
			}

			GUILayout.Space(8f);
			EditorGUI.BeginDisabledGroup(!IsAudioCaptured());

			if (ShowMediaItemList("Audio Codec", CodecManager.AudioCodecs, _audioCodec, _videoCodec))
			{
				_queueConfigureAudioCodec = _audioCodec;
			}

#if UNITY_EDITOR_WIN
			if (_audioCodec != null  && (_audioCodec.Name.EndsWith("MPEG Layer-3")))
			{
				GUI.color = Color.yellow;
				GUILayout.TextArea("Warning: We have had reports that this codec doesn't work. Consider using a different codec");
				GUI.color = Color.white;
			}
#endif
			EditorGUI.EndDisabledGroup();
		}

		private void DrawConfigGUI_MotionBlur()
		{
			EditorGUI.BeginDisabledGroup(IsCaptureRealTime());
			//EditorGUILayout.BeginVertical("box");
			//EditorGUI.indentLevel++;

			GUILayout.Space(8f);
			GUILayout.Label("Motion Blur (beta)", EditorStyles.boldLabel);
			//EditorGUILayout.BeginVertical("box");
			//EditorGUI.indentLevel++;

			if (IsCaptureRealTime())
			{
				GUI.color = Color.yellow;
				GUILayout.TextArea("Motion Blur only available in Offline Render mode");
				GUI.color = Color.white;
			}

			_useMotionBlur = EditorGUILayout.Toggle("Use Motion Blur", _useMotionBlur);
			EditorGUI.BeginDisabledGroup(!_useMotionBlur);
			EditorGUILayout.PrefixLabel("Samples");
			EditorGUILayout.BeginHorizontal(GUILayout.ExpandWidth(false));
			_motionBlurSampleCount = EditorGUILayout.IntSlider(_motionBlurSampleCount, 0, 64);
			EditorGUILayout.EndHorizontal();
			EditorGUI.EndDisabledGroup();

			//EditorGUI.indentLevel--;
			//EditorGUILayout.EndVertical();
			EditorGUI.EndDisabledGroup();
		}

		private void DrawConfigGUI_Audio()
		{
			bool showAudioSources = true;
			if (_outputTarget != OutputTarget.VideoFile)
			{
				GUI.color = Color.yellow;
				GUILayout.TextArea("Audio capture only available when outputing to video file");
				GUI.color = Color.white;
				showAudioSources = false;
			}

			if (showAudioSources)
			{
				EditorGUILayout.BeginVertical("box");
				_audioCaptureSource = (AudioCaptureSource)EditorGUILayout.EnumPopup("Audio Source", _audioCaptureSource);

				if (_audioCaptureSource != AudioCaptureSource.None)
				{
					bool showAudioOptions = true;

					#if AVPRO_MOVIECAPTURE_OFFLINE_AUDIOCAPTURE
					if (_audioCaptureSource != AudioCaptureSource.Manual && _audioCaptureSource != AudioCaptureSource.Unity && !IsCaptureRealTime())
					{
						GUI.color = Color.yellow;
						GUILayout.TextArea("Only Manual and Unity Audio Sources are available in offline capture mode");
						GUI.color = Color.white;
						showAudioOptions = false;
					}
					#else
					if (_audioCaptureSource != AudioCaptureSource.Manual && !IsCaptureRealTime())
					{
						GUI.color = Color.yellow;
						GUILayout.TextArea("Only Manual Audio Source is available in offline capture mode");
						GUI.color = Color.white;
						showAudioOptions = false;
					}
					#endif
					
					if (showAudioOptions)
					{
						if (_audioCaptureSource == AudioCaptureSource.Microphone)
						{
							GUILayout.Space(8f);

							ShowMediaItemList("Microphone", DeviceManager.AudioInputDevices, _audioInputDevice, _videoCodec);

							GUILayout.Space(8f);
						}
						else if (_audioCaptureSource == AudioCaptureSource.Manual)
						{
							EditorUtils.IntAsDropdown("Sample Rate", _propManualAudioSampleRate, EditorUtils.CommonAudioSampleRateNames, EditorUtils.CommonAudioSampleRateValues);
							EditorGUILayout.PropertyField(_propManualAudioChannelCount, new GUIContent("Channels"));
						}
					}
				}
				EditorGUILayout.EndVertical();
			}
		}

		private static string[] GetSuitableFileExtensions(Codec videoCodec, Codec audioCodec = null)
		{
			string[] result = null;
			if (videoCodec != null)
			{
				int audioCodecIndex = -1;
				if (audioCodec != null)
				{
					audioCodecIndex = audioCodec.Index;
				}
				result = NativePlugin.GetContainerFileExtensions(videoCodec.Index, audioCodecIndex);
			}
			if (result != null)
			{
				for (int i = 0; i < result.Length; i++)
				{
					result[i] = result[i].ToUpper();
				}
			}
			else
			{
				result = new string[0];
			}
			return result;
		}

		private static CaptureBase.DownScale GetDownScaleFromIndex(int index)
		{
			CaptureBase.DownScale result = CaptureBase.DownScale.Original;
			switch (index)
			{
				case 0:
					result = CaptureBase.DownScale.Original;
					break;
				case 1:
					result = CaptureBase.DownScale.Half;
					break;
				case 2:
					result = CaptureBase.DownScale.Quarter;
					break;
				case 3:
					result = CaptureBase.DownScale.Eighth;
					break;
				case 4:
					result = CaptureBase.DownScale.Sixteenth;
					break;
				case 5:
					result = CaptureBase.DownScale.Custom;
					break;
			}

			return result;
		}

		private static bool IsTrialVersion()
		{
			return NativePlugin.IsTrialVersion();
		}
	}
}
#endif